/*!
 * Class.js
 * Version 0.6.0
 *
 * Copyright(c) 2014 Gregory Jacobs <greg@greg-jacobs.com>
 * MIT Licensed. http://www.opensource.org/licenses/mit-license.php
 *
 * http://www.class-js.com
 */
;( function( root, factory ) {
	if( typeof define === 'function' && define.amd ) {
		define( factory );             // Define as AMD module if an AMD loader is present (ex: RequireJS).
	} else if( typeof exports !== 'undefined' ) {
		module.exports = factory();    // Define as CommonJS module for Node.js, if available.
	} else {
		root.Class = factory();        // Finally, define as a browser global if no module loader.
	}
}( this, function() {

	/*global window, ClassBuilder, Util */

	// Utility functions / variables.

	// NOTE: This entire source file is wrapped in a UMD block / factory function when built,
	//       so these are not global.


	// A variable, which is incremented, for assigning unique ID's to classes (constructor functions), allowing
	// for caching through lookups on hash maps
	var classIdCounter = 0;


	/**
	 * @class Class
	 * @singleton
	 *
	 * Simple utility for powerful JavaScript class creation. This provides a number of features for OOP in JavaScript, including:
	 *
	 * - Single inheritance with subclasses (like Java, C#, etc.)
	 * - Mixin classes
	 * - Static methods, which can optionally be automatically inherited by subclasses
	 * - A static method which is placed on classes that are created, which can be used to determine if the *class* is a subclass of
	 *   another (unlike the `instanceof` operator, which checks if an *instance* is a subclass of a given class).
	 * - An `instanceOf()` method, which should be used instead of the JavaScript `instanceof` operator, to determine if the instance
	 *   is an instance of a provided class, superclass, or mixin (the JavaScript `instanceof` operator only covers the first two).
	 * - The ability to add static methods while creating/extending a class, right inside the definition using special properties `statics`
	 *   and `inheritedStatics`. The former only applies properties to the class being created, while the latter applies properties to the
	 *   class being created, and all subclasses which extend it. (Note that the keyword for this had to be `statics`, and not `static`, as
	 *   `static` is a reserved word in Javascript).
	 * - A special static method, onClassCreated(), which can be placed in either the `statics` or `inheritedStatics` section, that is
	 *   executed after the class has been extended.
	 *
	 * Note that this is not the base class of all `Class` classes. It is a utility to create classes, and extend other classes. The
	 * fact that it is not required to be at the top of any inheritance hierarchy means that you may use it to extend classes from
	 * other frameworks and libraries, with all of the features that this implementation provides.
	 *
	 * Simple example of creating classes:
	 *
	 *     var Animal = Class.create( {
	 *         constructor : function( name ) {
	 *             this.name = name;
	 *         },
	 *
	 *         sayHi : function() {
	 *             alert( "Hi, my name is: " + this.name );
	 *         },
	 *
	 *         eat : function() {
	 *             alert( this.name + " is eating" );
	 *         }
	 *     } );
	 *
	 *
	 *     var Dog = Animal.extend( {
	 *         // Override sayHi method from superclass
	 *         sayHi : function() {
	 *             alert( "Woof! My name is: " + this.name );
	 *         }
	 *     } );
	 *
	 *     var Cat = Animal.extend( {
	 *         // Override sayHi method from superclass
	 *         sayHi : function() {
	 *             alert( "Meow! My name is: " + this.name );
	 *         }
	 *     } );
	 *
	 *
	 *     var dog1 = new Dog( "Lassie" );
	 *     var dog2 = new Dog( "Bolt" );
	 *     var cat = new Cat( "Leonardo Di Fishy" );
	 *
	 *     dog1.sayHi();  // "Woof! My name is: Lassie"
	 *     dog2.sayHi();  // "Woof! My name is: Bolt"
	 *     cat.sayHi();   // "Meow! My name is: Leonardo Di Fishy"
	 *
	 *     dog1.eat();  // "Lassie is eating"
	 *     dog2.eat();  // "Bolt is eating"
	 *     cat.eat();   // "Leonardo Di Fishy is eating"
	 */
	var Class = {

		/**
		 * A function which can be referenced from class definition code to specify an abstract method.
		 * This property (a function) simply throws an error if called, meaning that the method must be overridden
		 * in a subclass. This allows the function to be used to create interfaces.
		 *
		 * This is also a special reference where if an abstract class references this property, a concrete subclass
		 * must implement the method.
		 *
		 * Ex:
		 *
		 *     var AbstractClass = Class.create( {
		 *         abstractClass: true,
		 *
		 *         myMethod : Class.abstractMethod
		 *     } );
		 *
		 *     var SubClass = AbstractClass.extend( {
		 *         myMethod : function() {   // if this implementation method was missing, code would throw an error
		 *             ...
		 *         }
		 *     } );
		 *
		 * @property {Function} abstractMethod
		 */
		abstractMethod : function() {
			throw new Error( "method must be implemented in subclass" );
		},


		/**
		 * Creates a new class. Equivalent to calling `Class.extend( Object, { ... } )`.
		 * See {@link #extend} for more details.
		 *
		 * Example:
		 *
		 *     var Animal = Class.create( {
		 *         // class definition here
		 *     } );
		 *
		 * Example with `name` argument (see below):
		 *
		 *     var Animal = Class.create( 'Animal', {
		 *         // class definition here
		 *     } );
		 *
		 * @param {String} [name] A name for the class. It is recommended that you provide this, as this is set to the constructor's
		 *   `displayName` property to assist in debugging (esp. on Firefox), and is also used in error messages for subclasses
		 *   not implementing abstract methods, orphaned "override" methods, etc.
		 * @param {Object} classDefinition The class definition. See the `overrides` parameter of {@link #extend}.
		 * @return {Function} The generated class (constructor function).
		 */
		create : function( name, classDefinition ) {
			if( typeof name !== 'string' ) {
				classDefinition = name;
				name = "";
			}

			return Class.extend( name, Object, classDefinition );
		},


		/**
		 * Extends one class to create a subclass of it based on a passed object literal (`overrides`), and optionally any mixin
		 * classes that are desired.
		 *
		 * This method adds a few methods to the class that it creates:
		 *
		 * - override : Method that can be used to override members of the class with a passed object literal.
		 *   Same as {@link #override}, without the first argument.
		 * - extend : Method that can be used to directly extend the class. Same as this method, except without
		 *   the first argument.
		 * - hasMixin : Method that can be used to find out if the class (or any of its superclasses) implement a given mixin.
		 *   Accepts one argument: the class (constructor function) of the mixin. Note that it is preferable to check if a given
		 *   object is an instance of another class or has a mixin by using the {@link #isInstanceOf} method. This hasMixin()
		 *   method will just determine if the class has a given mixin, and not if it is an instance of a superclass, or even an
		 *   instance of itself.
		 *
		 *
		 * For example, to create a subclass of MySuperclass:
		 *
		 *     MyComponent = Class.extend( MySuperclass, {
		 *
		 *         constructor : function( config ) {
		 *             // Call superclass constructor
		 *             MyComponent.superclass.constructor.call( this, config );
		 *
		 *             // Your postprocessing here
		 *         },
		 *
		 *         // extension of another method (assuming MySuperclass had this method)
		 *         someMethod : function() {
		 *             // some preprocessing, if needed
		 *
		 *             MyComponent.superclass.someMethod.apply( this, arguments );  // send all arguments to superclass method
		 *
		 *             // some post processing, if needed
		 *         },
		 *
		 *         // a new method for this subclass (not an extended method)
		 *         yourMethod: function() {
		 *             // implementation
		 *         }
		 *     } );
		 *
		 * This is an example of creating a class with a mixin called MyMixin:
		 *
		 *     MyComponent = Class.extend( Class.util.Observable, {
		 *         mixins : [ MyMixin ],
		 *
		 *         constructor : function( config ) {
		 *             // Call superclass constructor
		 *             MyComponent.superclass.constructor.call( this, config );
		 *
		 *             // Call the mixin's constructor
		 *             MyMixin.constructor.call( this );
		 *
		 *             // Your postprocessing here
		 *         },
		 *
		 *
		 *         // properties/methods of the mixin will be added automatically, if they don't exist already on the class
		 *
		 *
		 *         // method that overrides or extends a mixin's method
		 *         mixinMethod : function() {
		 *             // call the mixin's method, if desired
		 *             MyMixin.prototype.mixinMethod.call( this );
		 *
		 *             // post processing
		 *         }
		 *
		 *     } );
		 *
		 * Note that calling superclass methods can be done with either the [Class].superclass or [Class].__super__ property.
		 *
		 * @param {String} [name] A name for the new subclass. It is recommended that you provide this, as this is set to the constructor's
		 *   `displayName` property to assist in debugging (esp. on Firefox), and is also used in error messages for subclasses
		 *   not implementing abstract methods, orphaned "override" methods, etc.
		 * @param {Function} superclass The constructor function of the class being extended. If making a brand new class with no superclass, this may
		 *   either be omitted, or provided as `Object`.
		 * @param {Object} overrides An object literal with members that make up the subclass's properties/method. These are copied into the subclass's
		 *   prototype, and are therefore shared between all instances of the new class. This may contain a special member named
		 *   `constructor`, which is used to define the constructor function of the new subclass. If this property is *not* specified,
		 *   a constructor function is generated and returned which just calls the superclass's constructor, passing on its parameters.
		 *   **It is essential that you call the superclass constructor in any provided constructor.** See example code.
		 * @return {Function} The subclass constructor from the `overrides` parameter, or a generated one if not provided.
		 */
		extend : function( name, superclass, overrides ) {
			var args = Array.prototype.slice.call( arguments );

			if( typeof name !== 'string' ) {
				args.unshift( "" );  // `name` arg
			}

			// The second argument may be omitted, making Object the superclass
			if( args.length === 2 ) {
				args.splice( 1, 0, Object );
			}

			name = args[ 0 ];
			superclass = args[ 1 ];
			overrides = args[ 2 ];

			return ClassBuilder.build( name, superclass, overrides );
		},


		/**
		 * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
		 * Usage:
		 *
		 *     Class.override( MyClass, {
		 *         newMethod1 : function() {
		 *             // etc.
		 *         },
		 *         newMethod2 : function( foo ) {
		 *             // etc.
		 *         }
		 *     } );
		 *
		 * @param {Object} origclass The class to override
		 * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
		 * containing one or more methods.
		 */
		override : function( origclass, overrides ) {
			if( overrides ) {
				var proto = origclass.prototype;
				Util.assign( proto, overrides );

				if( Util.isIe() && overrides.hasOwnProperty( 'toString' ) ) {
					proto.toString = overrides.toString;
				}
			}
		},



		/**
		 * Determines if a given object (`obj`) is an instance of a given class (`jsClass`). This method will
		 * return true if the `obj` is an instance of the `jsClass` itself, if it is a subclass of the `jsClass`,
		 * or if the `jsClass` is a mixin on the `obj`. For more information about classes and mixins, see the
		 * {@link #extend} method.
		 *
		 * @param {Mixed} obj The object (instance) to test.
		 * @param {Function} jsClass The class (constructor function) of which to see if the `obj` is an instance of, or has a mixin of.
		 * @return {Boolean} True if the obj is an instance of the jsClass (it is a direct instance of it,
		 *   it inherits from it, or the jsClass is a mixin of it)
		 */
		isInstanceOf : function( obj, jsClass ) {
			if( typeof jsClass !== 'function' ) {
				throw new Error( "jsClass argument of isInstanceOf method expected a Function (constructor function) for a JavaScript class" );
			}

			if( !Util.isObject( obj ) ) {
				return false;
			} else if( obj instanceof jsClass ) {
				return true;
			} else if( Class.hasMixin( obj.constructor, jsClass ) ) {
				return true;
			} else {
				return false;
			}
		},


		/**
		 * Determines if a class (i.e. constructor function) is, or is a subclass of, the given `baseClass`.
		 *
		 * The order of the arguments follows how {@link #isInstanceOf} accepts them (as well as the JavaScript
		 * `instanceof` operator. Try reading it as if there was a `subclassof` operator, i.e. `subcls subclassof supercls`.
		 *
		 * Example:
		 *     var Superclass = Class.create( {} );
		 *     var Subclass = Superclass.extend( {} );
		 *
		 *     Class.isSubclassOf( Subclass, Superclass );   // true - Subclass is derived from (i.e. extends) Superclass
		 *     Class.isSubclassOf( Superclass, Superclass ); // true - Superclass is the same class as itself
		 *     Class.isSubclassOf( Subclass, Subclass );     // true - Subclass is the same class as itself
		 *     Class.isSubclassOf( Superclass, Subclass );   // false - Superclass is *not* derived from Subclass
		 *
		 * @param {Function} subclass The class to test.
		 * @param {Function} superclass The class to test against.
		 * @return {Boolean} True if the `subclass` is derived from `superclass` (or is equal to `superclass`), false otherwise.
		 */
		isSubclassOf : function( subclass, superclass ) {
			if( typeof subclass !== 'function' || typeof superclass !== 'function' ) {
				return false;

			} else if( subclass === superclass ) {
				// `subclass` is `superclass`, return true
				return true;

			} else {
				// Walk the prototype chain of `subclass`, looking for `superclass`
				var currentClass = subclass,
				    currentClassProto = currentClass.prototype;

				while( ( currentClass = ( currentClassProto = currentClass.__super__ ) && currentClassProto.constructor ) ) {  // extra set of parens to get JSLint to stop complaining about an assignment inside a while expression
					if( currentClassProto.constructor === superclass ) {
						return true;
					}
				}
			}

			return false;
		},



		/**
		 * Determines if a class has a given mixin. Note: Most likely, you will want to use {@link #isInstanceOf} instead,
		 * as that will tell you if the given class either extends another class, or either has, or extends a class with
		 * a given mixin.
		 *
		 * @param {Function} classToTest
		 * @param {Function} mixinClass
		 * @return {Boolean}
		 */
		hasMixin : function( classToTest, mixinClass ) {
			// Assign the mixinClass (the class we're looking for as a mixin) an ID if it doesn't yet have one. This is done
			// here (instead of in extend()) so that any class can be used as a mixin, not just ones extended from Class.js)
			var mixinClassId = mixinClass.__Class_classId;
			if( !mixinClassId ) {
				mixinClassId = mixinClass.__Class_classId = ++classIdCounter;  // classIdCounter is from outer anonymous function of this class, and is used to assign a unique ID
			}

			// Create a cache for quick re-lookups of the mixin on this class
			var hasMixinCache = classToTest.__Class_hasMixinCache;
			if( !hasMixinCache ) {
				hasMixinCache = classToTest.__Class_hasMixinCache = {};
			}

			// If we have the results of a call to this method for this mixin already, returned the cached result
			if( mixinClassId in hasMixinCache ) {
				return hasMixinCache[ mixinClassId ];

			} else {
				// No cached result from a previous call to this method for the mixin, do the lookup
				var mixins = classToTest.mixins,
				    superclass = ( classToTest.superclass || classToTest.__super__ );

				// Look for the mixin on the classToTest, if it has any
				if( mixins ) {
					for( var i = 0, len = mixins.length; i < len; i++ ) {
						if( mixins[ i ] === mixinClass ) {
							return ( hasMixinCache[ mixinClassId ] = true );  // mixin was found, cache the result and return
						}
					}
				}

				// mixin wasn't found on the classToTest, check its superclass for the mixin (if it has one)
				if( superclass && superclass.constructor && superclass.constructor !== Object ) {
					var returnValue = Class.hasMixin( superclass.constructor, mixinClass );
					return ( hasMixinCache[ mixinClassId ] = returnValue );  // cache the result from the call to its superclass, and return that value

				} else {
					// mixin wasn't found, and the class has no superclass, cache the result and return false
					return ( hasMixinCache[ mixinClassId ] = false );
				}
			}
		}

	};
	/*global Class, Util */

	// A regular expression which is used to determine if a method calls its superclass method (using this._super())
	var superclassMethodCallRegex = /xyz/.test( function(){ var a = "xyz"; } ) ? /\b_super\b/ : /.*/;  // a regex to see if the _super() method is called within a function, for JS implementations that allow a function's text to be converted to a string. Note, need to keep the "xyz" as a string, so minifiers don't re-write it.


	// inline override() function which is attached to subclass constructor functions
	var inlineOverride = function( obj ) {
		for( var p in obj ) {
			this[ p ] = obj[ p ];
		}
	};


	/**
	 * @private
	 * @class Class.ClassBuilder
	 * @singleton
	 *
	 * Performs the functionality of building a class. Used from {@link Class#extend}.
	 */
	var ClassBuilder = {

		/**
		 * Builds a class from the `superclass` and `overrides` for the subclass.
		 *
		 * @param {String} name A string name for the class.
		 * @param {Function} superclass The constructor function of the class being extended.
		 * @param {Object} overrides An object literal with members that make up the subclass's properties/method.
		 * @return {Function} The subclass constructor from the `overrides` parameter, or a generated one if not provided.
		 */
		build : function( name, superclass, overrides ) {
			// Grab any special properties from the overrides, and then delete them (except for `abstractClasss`) so that they
			// aren't applied to the subclass's prototype when we copy all of the 'overrides' properties there
			var abstractClass = !!overrides.abstractClass,
			    statics = overrides.statics,
			    inheritedStatics = overrides.inheritedStatics,
			    mixins = overrides.mixins;

			delete overrides.statics;
			delete overrides.inheritedStatics;
			delete overrides.mixins;

			// --------------------------

			// Before creating the new subclass pre-process the methods of the subclass (defined in "overrides") to add the
			// this._super() method for methods that can call their associated superclass method. This should happen before
			// defining the new subclass, so that the constructor function can be wrapped as well.
			ClassBuilder.wrapSuperclassCallingMethods( superclass, overrides );

			// --------------------------


			// Now that preprocessing is complete, define the new subclass's constructor *implementation* function.
			// This is going to be wrapped in the actual subclass's constructor
			// This will be the actual implementation of the subclass's constructor (which the user defines), or a default
			// constructor which simply calls the superclass's constructor.
			var subclassCtorImplFn = ClassBuilder.createConstructor( superclass, overrides );


			// Create the actual subclass's constructor, which tests to see if the class being instantiated is abstract,
			// and if not, calls the subclassCtorFn implementation function
			var subclass = function() {
				var proto = this.constructor.prototype;
				if( proto.hasOwnProperty( 'abstractClass' ) && proto.abstractClass === true ) {
					var displayName = this.constructor.displayName;
					throw new Error( "Error: Cannot instantiate abstract class" + ( displayName ? " '" + displayName + "'" : "" ) );
				}

				// Call the actual constructor's implementation
				return subclassCtorImplFn.apply( this, arguments );
			};

			// Name provided, populate the special `displayName` property. Only supported by FF at the time of writing,
			// but may gain more support as time goes on.
			if( name ) {
				subclass.displayName = name;
			}

			ClassBuilder.createPrototypeChain( superclass, subclass );
			ClassBuilder.attachCommonSubclassStatics( subclass );
			ClassBuilder.attachCommonSubclassInstanceMethods( superclass, subclass );


			// Finally, add the properties/methods defined in the "overrides" config (which is basically the subclass's
			// properties/methods) onto the subclass prototype now.
			Class.override( subclass, overrides );


			// -----------------------------------

			// Check that if it is a concrete (i.e. non-abstract) class, that all abstract methods have been implemented
			// (i.e. that the concrete class overrides any `Class.abstractMethod` functions from its superclass)
			if( !abstractClass ) {
				ClassBuilder.checkAbstractMethodsImplemented( subclass );
			}

			// -----------------------------------

			// Now apply inherited statics to the class. Inherited statics from the superclass are first applied,
			// and then all overrides (so that subclasses's inheritableStatics take precedence)
			if( inheritedStatics || superclass.__Class_inheritedStatics ) {
				inheritedStatics = Util.assign( {}, superclass.__Class_inheritedStatics, inheritedStatics );  // inheritedStatics takes precedence of the superclass's inherited statics
				Util.assign( subclass, inheritedStatics );
				subclass.__Class_inheritedStatics = inheritedStatics;  // store the inheritedStatics for the next subclass
			}

			// Now apply statics to the class. These statics should override any inheritableStatics for the current subclass.
			// However, the inheritableStatics will still affect subclasses of this subclass.
			if( statics ) {
				Util.assign( subclass, statics );
			}


			// Handle mixins by applying their methods/properties to the subclass prototype. Methods defined by
			// the class itself will not be overwritten, and the later defined mixins take precedence over earlier
			// defined mixins. (Moving backwards through the mixins array to have the later mixin's methods/properties
			// take priority)
			if( mixins ) {
				ClassBuilder.applyMixins( subclass, mixins );
			}


			// If there is a static `onClassCreate` method, call it now with the new subclass as the argument
			var onClassCreate = subclass.onClassCreate || subclass.onClassCreated || subclass.onClassExtended;  // `onClassCreated` and `onClassExtended` are accepted for backward compatibility
			if( onClassCreate ) {
				onClassCreate.call( subclass, subclass );  // call in the scope of `subclass`, and with the `subclass` as the first argument
			}

			return subclass;
		},



		/**
		 * @private
		 * @param {Function} superclass
		 * @param {Object} overrides
		 */
		createConstructor : function( superclass, overrides ) {
			var subclassCtorImplFn;

			if( overrides.constructor !== Object ) {
				subclassCtorImplFn = overrides.constructor;
				delete overrides.constructor;  // Remove 'constructor' property from overrides here, so we don't accidentally re-apply it to the subclass prototype when we copy all properties over
			} else {
				subclassCtorImplFn = ( superclass === Object ) ? function(){} : function() { return superclass.apply( this, arguments ); };   // create a "default constructor" that automatically calls the superclass's constructor, unless the superclass is Object (in which case we don't need to, as we already have a new object)
			}

			return subclassCtorImplFn;
		},


		/**
		 * @private
		 * @param {Function} superclass
		 * @param {Function} subclass
		 */
		createPrototypeChain : function( superclass, subclass ) {
			var superclassPrototype = superclass.prototype,
			    subclassPrototype = subclass.prototype;

			var F = function() {};
			F.prototype = superclassPrototype;
			subclassPrototype = subclass.prototype = new F();  // set up prototype chain
			subclassPrototype.constructor = subclass;          // fix constructor property
			subclass.superclass = subclass.__super__ = superclassPrototype;
			subclass.__Class = true;  // a flag for testing if a given function is a class or not
		},


		/**
		 * @private
		 * @param {Function} subclass
		 */
		checkAbstractMethodsImplemented : function( subclass ) {
			var subclassPrototype = subclass.prototype;

			for( var methodName in subclassPrototype ) {
				if( subclassPrototype[ methodName ] === Class.abstractMethod ) {  // NOTE: Do *not* filter out prototype properties; we want to test them
					var displayName = subclassPrototype.constructor.displayName;
					if( subclassPrototype.hasOwnProperty( methodName ) ) {
						throw new Error( "The class " + ( displayName ? "'" + displayName + "'" : "being created" ) + " has abstract method '" + methodName + "', but is not declared with 'abstractClass: true'" );
					} else {
						throw new Error( "The concrete subclass " + ( displayName ? "'" + displayName + "'" : "being created" ) + " must implement abstract method: '" + methodName + "', or be declared abstract as well (using 'abstractClass: true')" );
					}
				}
			}
		},


		/**
		 * @private
		 * @param {Function} superclass
		 * @param {Object} overrides
		 */
		wrapSuperclassCallingMethods : function( superclass, overrides ) {
			var superclassPrototype = superclass.prototype;

			// Wrap all methods that use this._super() in the function that will allow this behavior (defined above), except
			// for the special 'constructor' property, which needs to be handled differently for IE (done below).
			for( var prop in overrides ) {
				if(
				    prop !== 'constructor' &&                               // We process the constructor separately, below (which is needed for IE, because IE8 and probably all versions below it won't enumerate it in a for-in loop, for whatever reason...)
				    overrides.hasOwnProperty( prop ) &&                     // Make sure the property is on the overrides object itself (not a prototype object)
				    typeof overrides[ prop ] === 'function' &&              // Make sure the override property is a function (method)
				    typeof superclassPrototype[ prop ] === 'function' &&    // Make sure the superclass has the same named function (method)
				    !overrides[ prop ].hasOwnProperty( '__Class' ) &&       // We don't want to wrap a constructor function of another class being provided as a prototype property to the class being created
				    superclassMethodCallRegex.test( overrides[ prop ] )     // And check to see if the string "_super" exists within the override function
				) {
					overrides[ prop ] = ClassBuilder.createSuperclassCallingMethod( superclass, prop, overrides[ prop ] );
				}
			}

			// Process the constructor on its own, here, because IE8 (and probably all versions below it) will not enumerate it
			// in the for-in loop above (for whatever reason...)
			if(
			    overrides.hasOwnProperty( 'constructor' ) &&  // make sure we don't get the constructor property from Object
			    typeof overrides.constructor === 'function' &&
			    typeof superclassPrototype.constructor === 'function' &&
			    superclassMethodCallRegex.test( overrides.constructor )
			) {
				overrides.constructor = ClassBuilder.createSuperclassCallingMethod( superclass, 'constructor', overrides.constructor );
			}
		},


		/**
		 * @private
		 * @param {Function} subclass
		 */
		attachCommonSubclassStatics : function( subclass ) {
			// Attach new static methods to the subclass
			subclass.override = function( overrides ) { Class.override( subclass, overrides ); };
			subclass.extend = function( name, overrides ) {
				if( arguments.length === 1 ) {
					overrides = name;
					name = "";
				}
				return Class.extend( name, subclass, overrides );
			};
			subclass.hasMixin = function( mixin ) { return Class.hasMixin( subclass, mixin ); };
		},


		/**
		 * Attach new instance methods to the subclass
		 *
		 * @private
		 * @param {Function} superclass
		 * @param {Function} subclass
		 */
		attachCommonSubclassInstanceMethods : function( superclass, subclass ) {
			var subclassPrototype = subclass.prototype;
			subclassPrototype.superclass = subclassPrototype.supr = function() { return superclass.prototype; };
			subclassPrototype.override = inlineOverride;   // inlineOverride function defined above
			subclassPrototype.hasMixin = function( mixin ) { return Class.hasMixin( this.constructor, mixin ); };
		},


		/**
		 * Creates a function that wraps methods of the new subclass that can call their superclass method.
		 *
		 * @private
		 * @param {Function} superclass
		 * @param {String} fnName
		 * @param {Function} fn
		 */
		createSuperclassCallingMethod : function( superclass, fnName, fn ) {
			var superclassPrototype = superclass.prototype;

			return function() {
				var tmpSuper = this._super,  // store any current _super reference, so we can "pop it off the stack" when the method returns
				    scope = this;

				// Add the new _super() method that points to the superclass's method
				this._super = function( args ) {  // args is an array (or arguments object) of arguments
					return superclassPrototype[ fnName ].apply( scope, args || [] );
				};

				// Now call the target method
				var returnVal = fn.apply( this, arguments );

				// And finally, restore the old _super reference, as we leave the stack context
				this._super = tmpSuper;

				return returnVal;
			};
		},


		/**
		 * @private
		 * @param {Function} subclass
		 * @param {Function[]} mixins
		 */
		applyMixins : function( subclass, mixins ) {
			var subclassPrototype = subclass.prototype;

			for( var i = mixins.length-1; i >= 0; i-- ) {
				var mixinPrototype = mixins[ i ].prototype;
				for( var prop in mixinPrototype ) {
					// Do not overwrite properties that already exist on the prototype
					if( typeof subclassPrototype[ prop ] === 'undefined' ) {
						subclassPrototype[ prop ] = mixinPrototype[ prop ];
					}
				}
			}

			// Store which mixin classes the subclass has. This is used in the hasMixin() method
			subclass.mixins = mixins;
		}

	};
	/*global window, Class */

	// NOTE: This source file is wrapped in a UMD block / factory function when built,
	//       so these are not global.

	/**
	 * @private
	 * @class Class.Util
	 * @singleton
	 *
	 * A few utility functions for Class.js
	 */
	var Util = Class.Util = {

		/**
		 * Assigns (shallow copies) the properties of `src` onto `dest`.
		 *
		 * @param {Object} dest The destination object.
		 * @param {...Object} src The source object(s). They are processed in order from left to right.
		 * @return {Object} The destination object.
		 */
		assign : function( dest ) {
			var srcObjects = Array.prototype.slice.call( arguments, 1 );

			for( var i = 0, len = srcObjects.length; i < len; i++ ) {
				var srcObj = srcObjects[ i ];

				for( var prop in srcObj ) {
					if( srcObj.hasOwnProperty( prop ) ) {
						dest[ prop ] = srcObj[ prop ];
					}
				}
			}
			return dest;
		},


		/**
		 * Determines if a value is an object.
		 *
		 * @param {Mixed} value
		 * @return {Boolean} `true` if the value is an object, `false` otherwise.
		 */
		isObject : function( value ) {
			return !!value && Object.prototype.toString.call( value ) === '[object Object]';
		},



		/**
		 * Determines if on Internet Explorer, for dealing with IE's toString() problem.
		 *
		 * @return {Boolean} `true` if the browser is IE, `false` otherwise.
		 */
		isIe : (function() {
			var isIe = false;
			if( typeof window !== 'undefined' && window.navigator && window.navigator.userAgent ) {
				var uA = window.navigator.userAgent.toLowerCase();
				isIe = /msie/.test( uA ) && !( /opera/.test( uA ) );
			}

			return function() { return isIe; };
		})()

	};

	return Class;

} ) );
var HQMap = {};
/**
 * 提供转换类方法
 * @module Util
 * @namespace HQMap
 * @class Convert
 */
HQMap.Convert = {
	/**
	 * 将经纬度数据转换为HQMap.LngLat对象
	 * @method toHQLngLat
	 * @param lng 经度数据
	 * @param lat 纬度数据
	 * @return HQMap.LngLat
	 */
	toHQLngLat: function(lng, lat) {
		return new HQMap.LngLat(lng, lat);
	},

	/**
	 * 将经纬度数据转换为HQMap.LngLat对象数组
	 * @method toHQLngLatArr
	 * @param datas  Array<Array> 经纬度二维数据， 二维数组中每个元素为一个一维的经纬度数据
	 * @return Array<HQMap.LngLat>
	 */
	toHQLngLatArr: function(datas) {
		var rs = [];
		if(datas && datas.length > 0) {
			for(var i=0; i<datas.length; i++) {
				var lnglat = new HQMap.LngLat(datas[i][0], datas[i][1]);
				rs.push(lnglat);
			}
		}
		return rs;
	},

	/**
	 * 将特定格式的经纬度数据串转换为Array<HQMap.LngLat>
	 * @method toHQLngLatArrByStr
	 * @param strData 经纬度数据串， "33.828,28.2;48,22344,98.3334"
	 * @return Array<HQMap.LngLat>
	 */
	toHQLngLatArrByStr: function(strData) {
		var rs = [];
		if(strData && strData.length > 0) {
			// 每一个坐标数据使用;分隔
			var lnglatArr = strData.split(";");
			for(var i=0; lnglatArr.length > 0; i++) {
				var lnglat = lnglatArr[i].split(",");
				// 每一个点位使用,分隔
				rs.push(new HQMap.LngLat(lnglat[0], lnglat[1]));
			}
		}
		return rs;
	},

	/**
	 * 将AMap LngLat 数据集转换为HQ LngLat 数据集
	 * @method toHQLngLatArrByAMapLngLatArr
	 * @param {Array} data - 数据集
	 * @return Array<HQMap.LngLat> 数据集
	 */
	toHQLngLatArrByAMapLngLatArr: function(data) {
		var rs = [];
		if(data && data instanceof Array && data.length > 0) {
			for(var i=0; i<data.length; i++) {
				rs.push(new HQMap.LngLat(data[i].lng, data[i].lat));
			}
		}
		return rs;
	}

};
/**
 * @class HashMap
 * @module Util
 * @namespace HQMap
 */
HQMap.HashMap = function() {
	this._entrys = {};
	this.length = 0;
};

HQMap.HashMap.prototype = {
	/**
	 * 添加元素
	 *@method put
	 *@param {String} key  键值
	 *@param {Object} value 值
	 */
	put : function( key, value ) {
		if( key ) {
			if( key in this._entrys ) {
				this._entrys[ key ] = value;
			} else {
				this._entrys[ key ] = value;
				this.length++;
			}
		}
	},

	/**
	 * 根据Key值获取元素
	 *@method get
	 *@param {String} key  键值
	 *@return Object
	 */
	get : function( key ) {
		return this._entrys[ key ];
	},

	/**
	 * 根据Key值删除元素
	 *@method remove
	 *@param {String} key  键值
	 *@return void
	 */
	remove : function( key ) {
		if( key ) {
			delete this._entrys[ key ];
			this.length--;
		}
	},

	/**
	 * 清空所有元素
	 *@method clear
	 *@return void
	 */
	clear : function() {
		this._entrys = {};
		this.length = 0;
	},

	/**
	 * 获取集合长度
	 *@method size
	 *@return Integer
	 */
	size : function() {
		return this.length;
	},

	/**
	  * 遍历集合
	  * @method each
	  * @param {Function} callback 回调函数
	  * @return void
	  */
	each : function( callback ) {
		for( var key in this._entrys ) {
			if( this._entrys.hasOwnProperty( key ) ) {
				callback.call( this, key, this._entrys[ key ] );
			}
		}
	}
};
/**
 * 地图缩放级别转换为地图显示比例,地图比例单位为米
 * @module Util
 * @namespace HQMap
 * @class Scale
 */

HQMap.Scale = Class.create( {

	statics : {

		/**
		 * 获取比例尺单位
		 * @method getScale
		 * @param level
		 * @return Number 米
		 */
		getScaleUnit: function(level) {
			var unit = 1000;
			switch (level){
		  		case 1:
		  		case 2:
				case 3:
					unit = 1000*1000;
					break;
				case 3:
					unit = 1000*1000;
					break;
				case 4:
					unit = 500*1000;
					break;
				case 5:
					unit = 200*1000;
					break;
				case 6:
					unit = 100*1000;
					break;
				case 7:
					unit = 100*1000;
					break;
				case 8:
					unit = 50*1000;
					break;
				case 9:
					unit = 20*1000;
					break;
				case 10:
					unit = 10*1000;
					break;
				case 11:
					unit = 5*1000;
					break;
				case 12:
					unit = 2*1000;
					break;
				case 13:
					unit = 1*1000;
					break;
				case 14:
					unit = 500;
					break;
				case 15:
					unit = 200;
					break;
				case 16:
					unit = 200;
					break;
				case 17:
					unit = 100;
					break;
				case 18:
					unit = 50;
					break;
		  		default:
		  			unit = 1000;
			  		break;
			}
			return unit;
		}
	}
});
/**
 * @class StringUtil
 * @module Util
 * @namespace HQMap
 */
HQMap.StringUtil = {
	/**
	  * 字符转换
	  * @method toFirstLetterUpperCase
	  * @param {String} str 待转换的字符数据
	  */
	toFirstLetterUpperCase: function(str) {
		if(str) {
			return str.replace(/^(\w)(\w+)/, function(v,v1,v2) {
				return v1.toUpperCase() + v2;
			});
		} else {
			return "";
		}
	}
};
/**
 * @class Validator
 * @module Util
 * @namespace HQMap
 */
HQMap.Validator = {
	/**
	 * 参数信息
	 * @property paramInfo
	 * @type Object --- name: '', value:''
	 */
	paramInfo: {
		name: "",
		value: null
	},

	/**
	 * 设置参数信息
	 * @method setParamInfo
	 * @param {Object} paramInfo 参数信息
	 * @return void
	 */
	setParamInfo: function(paramInfo) {
		this.paramInfo = paramInfo;
		return this;
	},

	/**
	 * 校验是否是字符串
	 * @method isString
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	isString: function(msg) {
		if($.type(this.paramInfo.value) !== "string") {
			throw new Error( msg || this.paramInfo.name + "必须是字符串");
		}
		return this;
	},

	/**
	 * 校验是否是函数
	 * @method isFunction
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	isFunction: function(msg) {
		if($.type(this.paramInfo.value) !== "function") {
			throw new Error( msg || this.paramInfo.name + "必须是函数");
		}
		return this;
	},

	/**
	 * 校验是否是对象
	 * @method isObject
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	isObject: function(msg) {
		if($.type(this.paramInfo.value) !== "object") {
			throw new Error( msg || this.paramInfo.name + "必须是函数");
		}
		return this;
	},

	/**
	 * 校验非空
	 * @method notNull
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	notNull: function(msg) {
		if(this.paramInfo.value === null) {
			throw new Error( msg || this.paramInfo.name + "必须不为null");
		}
		return this;
	},

	/**
	 * 校验非空
	 * @method notUndefined
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	notUndefined: function(msg) {
		if(this.paramInfo.value === undefined) {
			throw new Error( msg || this.paramInfo.name + "必须不为undefined");
		}
		return this;
	},

	/**
	 * 校验非空
	 * @method notEmpty
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	notEmpty: function(msg) {
		msg = msg || this.paramInfo.name + "必须存在";
		return this.notNull(msg).notUndefined(msg);
	},

	/**
	 * 校验非空
	 * @method notEmptyString
	 * @param {String} msg 提示信息
	 * @return Object
	 */
	notEmptyString: function(msg) {
		msg = msg || this.paramInfo.name + "必须不为空字符串";
		this.notEmpty(msg).isString(msg);
		if(!$.trim(this.paramInfo.value)) {
			throw new Error(msg);
		}
		return this;
	}
};
// VERSION: 2.3 LAST UPDATE: 11.07.2013
/*
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 *
 * Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009
 * Website: http://code.google.com/p/jqueryrotate/
 */
(function(k){for(var d,f,l=document.getElementsByTagName("head")[0].style,h=["transformProperty","WebkitTransform","OTransform","msTransform","MozTransform"],g=0;g<h.length;g++)void 0!==l[h[g]]&&(d=h[g]);d&&(f=d.replace(/[tT]ransform/,"TransformOrigin"),"T"==f[0]&&(f[0]="t"));eval('IE = "v"=="\v"');jQuery.fn.extend({rotate:function(a){if(0!==this.length&&"undefined"!=typeof a){"number"==typeof a&&(a={angle:a});for(var b=[],c=0,d=this.length;c<d;c++){var e=this.get(c);if(e.Wilq32&&e.Wilq32.PhotoEffect)e.Wilq32.PhotoEffect._handleRotation(a);
else{var f=k.extend(!0,{},a),e=(new Wilq32.PhotoEffect(e,f))._rootObj;b.push(k(e))}}return b}},getRotateAngle:function(){for(var a=[],b=0,c=this.length;b<c;b++){var d=this.get(b);d.Wilq32&&d.Wilq32.PhotoEffect&&(a[b]=d.Wilq32.PhotoEffect._angle)}return a},stopRotate:function(){for(var a=0,b=this.length;a<b;a++){var c=this.get(a);c.Wilq32&&c.Wilq32.PhotoEffect&&clearTimeout(c.Wilq32.PhotoEffect._timer)}}});Wilq32=window.Wilq32||{};Wilq32.PhotoEffect=function(){return d?function(a,b){a.Wilq32={PhotoEffect:this};
this._img=this._rootObj=this._eventObj=a;this._handleRotation(b)}:function(a,b){this._img=a;this._onLoadDelegate=[b];this._rootObj=document.createElement("span");this._rootObj.style.display="inline-block";this._rootObj.Wilq32={PhotoEffect:this};a.parentNode.insertBefore(this._rootObj,a);if(a.complete)this._Loader();else{var c=this;jQuery(this._img).bind("load",function(){c._Loader()})}}}();Wilq32.PhotoEffect.prototype={_setupParameters:function(a){this._parameters=this._parameters||{};"number"!==
typeof this._angle&&(this._angle=0);"number"===typeof a.angle&&(this._angle=a.angle);this._parameters.animateTo="number"===typeof a.animateTo?a.animateTo:this._angle;this._parameters.step=a.step||this._parameters.step||null;this._parameters.easing=a.easing||this._parameters.easing||this._defaultEasing;this._parameters.duration=a.duration||this._parameters.duration||1E3;this._parameters.callback=a.callback||this._parameters.callback||this._emptyFunction;this._parameters.center=a.center||this._parameters.center||
["50%","50%"];this._rotationCenterX="string"==typeof this._parameters.center[0]?parseInt(this._parameters.center[0],10)/100*this._imgWidth*this._aspectW:this._parameters.center[0];this._rotationCenterY="string"==typeof this._parameters.center[1]?parseInt(this._parameters.center[1],10)/100*this._imgHeight*this._aspectH:this._parameters.center[1];a.bind&&a.bind!=this._parameters.bind&&this._BindEvents(a.bind)},_emptyFunction:function(){},_defaultEasing:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-
1)+c},_handleRotation:function(a,b){d||this._img.complete||b?(this._setupParameters(a),this._angle==this._parameters.animateTo?this._rotate(this._angle):this._animateStart()):this._onLoadDelegate.push(a)},_BindEvents:function(a){if(a&&this._eventObj){if(this._parameters.bind){var b=this._parameters.bind,c;for(c in b)b.hasOwnProperty(c)&&jQuery(this._eventObj).unbind(c,b[c])}this._parameters.bind=a;for(c in a)a.hasOwnProperty(c)&&jQuery(this._eventObj).bind(c,a[c])}},_Loader:function(){return IE?function(){var a=
this._img.width,b=this._img.height;this._imgWidth=a;this._imgHeight=b;this._img.parentNode.removeChild(this._img);this._vimage=this.createVMLNode("image");this._vimage.src=this._img.src;this._vimage.style.height=b+"px";this._vimage.style.width=a+"px";this._vimage.style.position="absolute";this._vimage.style.top="0px";this._vimage.style.left="0px";this._aspectW=this._aspectH=1;this._container=this.createVMLNode("group");this._container.style.width=a;this._container.style.height=b;this._container.style.position=
"absolute";this._container.style.top="0px";this._container.style.left="0px";this._container.setAttribute("coordsize",a-1+","+(b-1));this._container.appendChild(this._vimage);this._rootObj.appendChild(this._container);this._rootObj.style.position="relative";this._rootObj.style.width=a+"px";this._rootObj.style.height=b+"px";this._rootObj.setAttribute("id",this._img.getAttribute("id"));this._rootObj.className=this._img.className;for(this._eventObj=this._rootObj;a=this._onLoadDelegate.shift();)this._handleRotation(a,
!0)}:function(){this._rootObj.setAttribute("id",this._img.getAttribute("id"));this._rootObj.className=this._img.className;this._imgWidth=this._img.naturalWidth;this._imgHeight=this._img.naturalHeight;var a=Math.sqrt(this._imgHeight*this._imgHeight+this._imgWidth*this._imgWidth);this._width=3*a;this._height=3*a;this._aspectW=this._img.offsetWidth/this._img.naturalWidth;this._aspectH=this._img.offsetHeight/this._img.naturalHeight;this._img.parentNode.removeChild(this._img);this._canvas=document.createElement("canvas");
this._canvas.setAttribute("width",this._width);this._canvas.style.position="relative";this._canvas.style.left=-this._img.height*this._aspectW+"px";this._canvas.style.top=-this._img.width*this._aspectH+"px";this._canvas.Wilq32=this._rootObj.Wilq32;this._rootObj.appendChild(this._canvas);this._rootObj.style.width=this._img.width*this._aspectW+"px";this._rootObj.style.height=this._img.height*this._aspectH+"px";this._eventObj=this._canvas;for(this._cnv=this._canvas.getContext("2d");a=this._onLoadDelegate.shift();)this._handleRotation(a,
!0)}}(),_animateStart:function(){this._timer&&clearTimeout(this._timer);this._animateStartTime=+new Date;this._animateStartAngle=this._angle;this._animate()},_animate:function(){var a=+new Date,b=a-this._animateStartTime>this._parameters.duration;if(b&&!this._parameters.animatedGif)clearTimeout(this._timer);else{if(this._canvas||this._vimage||this._img)a=this._parameters.easing(0,a-this._animateStartTime,this._animateStartAngle,this._parameters.animateTo-this._animateStartAngle,this._parameters.duration),
this._rotate(~~(10*a)/10);this._parameters.step&&this._parameters.step(this._angle);var c=this;this._timer=setTimeout(function(){c._animate.call(c)},10)}this._parameters.callback&&b&&(this._angle=this._parameters.animateTo,this._rotate(this._angle),this._parameters.callback.call(this._rootObj))},_rotate:function(){var a=Math.PI/180;return IE?function(a){this._angle=a;this._container.style.rotation=a%360+"deg";this._vimage.style.top=-(this._rotationCenterY-this._imgHeight/2)+"px";this._vimage.style.left=
-(this._rotationCenterX-this._imgWidth/2)+"px";this._container.style.top=this._rotationCenterY-this._imgHeight/2+"px";this._container.style.left=this._rotationCenterX-this._imgWidth/2+"px"}:d?function(a){this._angle=a;this._img.style[d]="rotate("+a%360+"deg)";this._img.style[f]=this._parameters.center.join(" ")}:function(b){this._angle=b;b=b%360*a;this._canvas.width=this._width;this._canvas.height=this._height;this._cnv.translate(this._imgWidth*this._aspectW,this._imgHeight*this._aspectH);this._cnv.translate(this._rotationCenterX,
this._rotationCenterY);this._cnv.rotate(b);this._cnv.translate(-this._rotationCenterX,-this._rotationCenterY);this._cnv.scale(this._aspectW,this._aspectH);this._cnv.drawImage(this._img,0,0)}}()};IE&&(Wilq32.PhotoEffect.prototype.createVMLNode=function(){document.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{return!document.namespaces.rvml&&document.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),function(a){return document.createElement("<rvml:"+a+' class="rvml">')}}catch(a){return function(a){return document.createElement("<"+
a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}}())})(jQuery);

/**
 * 经纬度矩形范围
 * @class Bounds
 * @module HQMap
 * @main HQMap
 * @submodule Common
 * @namespace HQMap
 */
HQMap.Bounds = Class.create( {


	/**
	 * 西南角经纬度
	 * @property southWest
     * @type HQMap.LngLat
	 */
	southWest: null,


	/**
	 * 东北角经纬度
	 * @property northEast
     * @type HQMap.LngLat
	 */
	northEast: null,


    /**
     * 构造函数
     *
     * @constructor
     * @param {HQMap.LngLat} southWest 西南角经纬度
     * @param {HQMap.LngLat} northEast 东北角经纬度
     */
    constructor : function( southWest, northEast ) {
    	this.southWest = southWest;
    	this.northEast = northEast;

    	this._ = new AMap.Bounds(
    		new AMap.LngLat( this.southWest.getLng(), this.southWest.getLat() ),
    		new AMap.LngLat( this.northEast.getLng(), this.northEast.getLat() )
    	);
    },


    /**
     * 设置西南角经纬度
     * @method setSouthWest
     * @param {HQMap.LngLat} southWest 经纬度
     */
	setSouthWest : function( southWest ) {
		this.southWest = southWest;

    	this._ = new AMap.Bounds(
    		new AMap.LngLat( this.southWest.getLng(), this.southWest.getLat() ),
    		new AMap.LngLat( this.northEast.getLng(), this.northEast.getLat() )
    	);
	},


	/**
	 * 获取西南角坐标
	 * @method getSouthWest
	 * @return HQMap.LngLat 经纬度坐标
	 */
	getSouthWest: function() {
		var southWest = this._.getSouthWest();
		this.southWest = new HQMap.LngLat( southWest.getLng(), southWest.getLat() );
		return this.southWest;
	},


    /**
     * 设置东北角经纬度
     * @method setNorthEast
     * @param {HQMap.LngLat} southWest 经纬度
     */
	setNorthEast : function( northEast ) {
		this.northEast = northEast;

    	this._ = new AMap.Bounds(
    		new AMap.LngLat( this.southWest.getLng(), this.southWest.getLat() ),
    		new AMap.LngLat( this.northEast.getLng(), this.northEast.getLat() )
    	);
	},


	/**
	 * 获取东北角坐标
	 * @method getNorthEast
	 * @return HQMap.LngLat
	 */
	getNorthEast : function() {
		var northEast = this._.getNorthEast();
		this.northEast = new HQMap.LngLat( northEast.getLng(), northEast.getLat() );
		return this.northEast;
	},


	/**
	 * 获取当前区域中心点经纬度
	 * @method getCenter
	 * @return HQMap.LngLat
	 */
	getCenter : function() {
		var centerLngLat = this._.getCenter();
		return new HQMap.LngLat( centerLngLat.getLng(), centerLngLat.getLat() );
	},


	/**
	 * 指定点是否在矩形范围内
	 * @method contains
	 * @param {HQMap.LngLat} point 点位信息
	 * @return Boolean
	 */
	contains : function( point ) {
		return this._.contains( point._ );
	},


	/**
	 * 以字符串形式返回地物对象的矩形范围
	 * @method toString
	 * @return String 经纬度的字符串表示 "西南角经度,西南角纬度;东北角经度,东北角纬度"
	 */
	toString: function() {
		return this.southWest.getLng() + "," + this.southWest.getLat() + ";" +
				this.northEast.getLng() + "," + this.northEast.getLat();
	}


} );
/**
 * @module HQMap
 * @namespace HQMap
 */
HQMap.DrivingPolicy = {
    LEAST_TIME : 0,
    LEAST_FEE : 1,
    LEAST_DISTANCE : 2,
    REAL_TRAFFIC : 4
}
/**
 * 图标
 * @class Icon
 * @module HQMap
 * @submodule Common
 * @namespace HQMap
 */
HQMap.Icon = Class.create( {


	/**
	 * 地址
	 * @property image
	 * @type String
	 */
	image: "",


	/**
	 * 尺寸
	 * @property size
	 * @type HQMap.Size
	 */
	size: null,


	/**
	 * 所用图片大小，根据所设置的大小拉伸或压缩图片，等同于CSS中的background-size属性
	 * @property imageSize
	 * @type HQMap.Size
	 */
	imageSize: null,


	/**
	 * 取图偏移量。当image中指定了一个大图时，可通过size和imageOffset配合，显示图标的指定范围
	 * @property imageOffset
     * @type HQMap.Pixel
	 */
	imageOffset: null,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {object} options 配置选项
	 */
    constructor : function( options ) {
    	$.extend( this, options || {} );
    	this._ = new AMap.Icon( this._getOptions() );
    },


    /**
     * 设置地址
     * @method setImage
     * @param {String} image 图片地址
     */
    setImage : function(image) {
		this.image = image;
		this._ = new AMap.Icon( this._getIconOptions() );
    },


    /**
     * 获取地址
     * @method getImage
     * @return String
     */
    getImage : function() {
    	return this.image;
    },


    /**
     * 设置尺寸
     * @method setSize
     * @param {HQMap.Size} size 尺寸大小
     */
    setSize : function( size ) {
		if( $.isArray( size ) ) {
			this.size = new HQMap.Size( size[ 0 ], size[ 1 ] );
		} else {
			this.size = size;
		}

		this._ = new AMap.Icon( this._getIconOptions() );
    },


    /**
     * 获取尺寸
     * @method getSize
     * @return HQMap.Size
     */
    getSize : function() {
    	return this.size;
    },


    /**
     * 设置图片尺寸
     * @method setImageSize
     * @param {HQMap.Size} imageSize 图片大小
     */
    setImageSize : function( imageSize ) {
		if( $.isArray( imageSize ) ) {
			this.imageSize = new HQMap.Size( imageSize[ 0 ], imageSize[ 1 ] );
		} else {
			this.imageSize = imageSize;
		}

		this._ = new AMap.Icon( this._getIconOptions() );
    },


    /**
     * 获取图片尺寸
     * @method getImageSize
     * @return HQMap.Size
     */
    getImageSize: function() {
    	return this.imageSize;
    },


    /**
     * 设置取图偏移量
     * @method setImageOffset
     * @param {HQMap.Pixel} imageOffset 图片偏移量
     */
    setImageOffset : function( imageOffset ) {
		if( $.isArray( imageOffset ) ) {
			this.imageOffset = new HQMap.Pixel( imageOffset[ 0 ], imageOffset[ 1 ] );
		} else {
			this.imageOffset = imageOffset;
		}

		this._ = new AMap.Icon( this._getIconOptions() );
    },


    /**
     * 获取取图偏移量
     * @method getImageOffset
     * @return HQMap.Pixel
     */
    getImageOffset: function() {
    	return this.imageOffset;
    },


    /**
     * 获取配置选项
     * @method _getOptions
     * @private
     * @return Object
     */
    _getOptions: function() {
		var iconOptions = {};
		if(this.image) {
			iconOptions.image = this.image;
		}
		if(this.size) {
			iconOptions.size = this.size;
		}
		if(this.imageSize) {
			iconOptions.imageSize = this.imageSize;
		}
		if(this.imageOffset) {
			iconOptions.imageOffset = this.imageOffset;
		}
		return iconOptions;
    }


} );
/**
 * 经纬度点
 * @class LngLat
 * @module HQMap
 * @namespace HQMap
 */
HQMap.LngLat = Class.create( {


	/**
	 * 经度
	 * @property lng
	 * @type Number
	 */
	lng: 0,


	/**
	 * 纬度
	 * @property lat
	 * @type Number
	 */
	lat: 0,


	/**
	 * 构造函数
	 * @constructor
	 * @param {number} lng - 经度
	 * @param {number} lat - 纬度
	 */
    constructor : function( lng, lat ) {
    	this.lng = lng;
    	this.lat = lat;

    	this._ = new AMap.LngLat( this.lng, this.lat );
    },


    /**
     * 设置经度
     * @method setLng
     * @param {Number} lng 经度
     */
	setLng : function( lng ) {
		this.lng = lng;
		this._ = new AMap.LngLat( this.lng, this.lat );
	},


	/**
	 * 获取经度
	 * @method getLng
	 * @return Number
	 */
	getLng : function() {
		this.lng = this._.getLng();
		return this.lng;
	},


    /**
     * 设置纬度
     * @method setLat
     * @param {Number} lat 纬度
     */
	setLat : function( lat ) {
		this.lat = lat;
		this._ = new AMap.LngLat( this.lng, this.lat );
	},


	/**
	 * 获取纬度
	 * @method getLat
	 * @return Number
	 */
	getLat : function() {
		this.lat = this._.getLat();
		return this.lat;
	},


	/**
	 * 当前经纬度坐标值经度移动w，纬度移动s，得到新的坐标。 经度向右移为正值，纬度向上移为正值，单位为米。
	 * @method offset
	 * @param {Number} w 经度移动距离
	 * @param {Number} s 纬度移动距离
	 * @return HQMap.LngLat 新的经纬度
	 */
	offset : function( w, s ) {
		var offsetLngLat = this._.offset( w, s );
		return new HQMap.LngLat( offsetLngLat.getLng(), offsetLngLat.getLat() );
	},


	/**
	 * 当前经纬度和传入经纬度之间的地面距离 单位为米
	 * @method distance
	 * @param {HQMap.LngLat} lnglat 位置信息
	 * @return Number
	 */
	distance : function( lnglat ) {
		return this._.distance( lnglat._ );
	},


	/**
	 * 当前经纬度与传入经纬度是否相等
	 * @method equals
	 * @param {HQMap.LngLat} lnglat
	 * @return Boolean
	 */
	equals : function( lnglat ) {
		return this.lng === lnglat.getLng() && this.lat === lnglat.getLat();
	},


	/**
	 * 以字符串形式返回经纬度
	 * @method toString
	 * @overwrite
	 * @return String 经纬度的字符串表示 "经度,纬度"
	 */
	toString : function() {
		return this.lng + "," + this.lat;
	}


} );
/**
 * 像素点
 * @class Pixel
 * @module HQMap
 * @namespace HQMap
 */
HQMap.Pixel = Class.create( {


	/**
	 * 横坐标
	 * @property x
	 * @type Number
	 */
	x: 0,


	/**
	 * 纵坐标
	 * @property y
	 * @type Number
	 */
	y: 0,


	/**
	 * 构造函数
	 * @constructor
	 * @param {Number} x 横坐标
	 * @param {Number} y 纵坐标
	 */
    constructor : function( x, y ) {
    	this.x = x;
    	this.y = y;

    	this._ = new AMap.Pixel( this.x, this.y );
    },


    /**
     * 设置横坐标
     * @method setX
     * @param {Number} x
     */
	setX : function( x ) {
		this.x = x;
		this._ = new AMap.Pixel( this.x, this.y );
	},


	/**
	 * 获得横坐标
	 * @method getX
	 * @return Number
	 */
	getX : function() {
		this.x = this._.getX();
		return this.x;
	},


    /**
     * 设置纵坐标
     * @method setY
     * @param {Number} x - 纵坐标
     */
	setY : function( y ) {
		this.y = y;
		this._ = new AMap.Pixel( this.x, this.y );
	},


	/**
	 * 获得纵坐标
	 * @method getY
	 * @return Number
	 */
	getY: function() {
		this.y = this._.getY();
		return this.y;
	},


	/**
	 * 当前像素坐标与传入像素坐标是否相等
	 * @method equals
	 * @param {HQMap.Pixel} pixel
	 * @return Boolean
	 */
	equals : function( pixel ) {
		return this.x === pixel.getX() && this.y === pixel.getY();
	},


	/**
	 * 以字符串形式返回像素坐标
	 * @method toString
	 * @override
	 * @return String 坐标的字符串表示 "横坐标,纵坐标"
	 */
	toString : function() {
		return this.x + "," + this.y;
	}


} );
/**
 * 尺寸
 * @class Size
 * @module HQMap
 * @namespace HQMap
 */
HQMap.Size = Class.create( {


	/**
	 * 宽度
	 * @property
	 * @type Number
	 */
	width: 0,


	/**
	 * 高度
	 * @property height
	 * @type Number
	 */
	height: 0,


	/**
	 * 构造函数
	 * @constructor
	 * @param {Number} width 宽度
	 * @param {Number} height 高度
	 */
    constructor : function( width, height ) {
    	this.width = width;
    	this.height = height;

    	this._ = new AMap.Size( this.width, this.height );
    },

    /**
     * 设置宽度
     * @method setWidth
     * @param {Number} width 宽度
     */
    setWidth : function( width ) {
    	this.width = width;
    	this._ = new AMap.Size( this.width, this.height );
    },


    /**
     * 获取宽度
     * @method getWidth
     * @return Number
     */
    getWidth : function() {
    	this.width = this._.getWidth();
    	return this.width;
    },


    /**
     * 设置高度
     * @method setHeight
     * @param {Number} height 高度
     */
    setHeight : function( height ) {
    	this.height = height;
    	this._ = new AMap.Size( this.width, this.height );
    },


    /**
     * 获取高度
     * @method getHeight
     * @return Number
     */
    getHeight : function() {
    	this.height = this._.getHeight();
    	return this.height;
    },


	/**
	 * 以字符串形式返回尺寸大小对象
	 * @method toString
	 * @overwrite
	 * @return String 尺寸大小的字符串表示 "宽度,高度"
	 */
	toString: function() {
		return this.width + "," + this.height;
	}


} );
/**
 * 事件对象类
 * @class EventObject
 * @module HQMap
 * @namespace HQMap
 */
HQMap.EventObject = Class.create( {


	/**
	 * 类型
	 * @property type
	 * @type String
	 */
	type : null,

	/**
	 * 覆盖物ID
	 * @property overlayId
	 * @type String
	 */
	overlayId : null,

	/**
     * 目标对象
	 * @property target
	 * @type Object
	 */
	target : null,


	/**
	 * 发生事件时光标所在处的经纬度坐标
	 * @property lnglat
	 * @type HQMap.LngLat
	 */
	lnglat : null,


	/**
	 * 发生事件时光标所在处的像素坐标
	 * @property pixel
	 * @type HQMap.Pixel
	 *
	 */
	pixel : null,


	/**
	 * 构造函数
	 * @method constructor
	 * @constructor
	 * @param {object} options - 配置选项
	 */
    constructor : function( options ) {
    	$.extend( this, options || {} );
    },


    /**
     * 设置类型
     * @method setType
     * @param {String} type 类型
     */
    setType : function( type ) {
    	this.type = type;
    },


    /**
     * 获取类型
     * @method getType
     * @return String
     */
    getType : function() {
    	return this.type;
    },


    /**
     * 设置事件发生的对象
     * @method setTarget
     * @param {Object} target
     */
    setTarget : function( target ) {
    	this.target = target;
    },


    /**
     * 获取事件发生的对象
     * @method getTarget
     * @return Object
     */
    getTarget : function() {
    	return this.target;
    },


    /**
     * 设置发生事件时光标所在处的经纬度坐标
     * @method setLngLat
     * @param {HQMap.LngLat} lnglat
     */
    setLngLat : function( lnglat ) {
    	this.lnglat = lnglat;
    },


    /**
     * 获取发生事件时光标所在处的经纬度坐标
     * @method getLngLat
     * @return HQMap.LngLat
     */
    getLngLat : function() {
    	return this.lnglat;
    },


    /**
     * 设置发生事件时光标所在处的像素坐标
     * @method setPixel
     * @param {HQMap.Pixel} pixel
     */
    setPixel : function( pixel ) {
    	this.pixel = pixel;
    },


    /**
     * 获取发生事件时光标所在处的像素坐标
     * @method getPixel
     * @return HQMap.Pixel
     */
    getPixel : function() {
    	return this.pixel;
    }


} );

/**
 * A mixin that can add very simple events functionality to a class
 * @class Observable
 * @module HQMap
 * @namespace HQMap
 */
HQMap.Observable = Class.create( {

	/**
	  * @constructor
	  */
    constructor : function() {
        this.events = {};
    },

    /**
     * 添加监听
     * @method addListener
     * @param {String} eventName 事件名称
	 * @param {Function} fn 回调
	 * @param {Object} scope 作用域
     */
    addListener : function( eventName, fn, scope ) {
        if( !this.events[ eventName ] ) {
            this.events[ eventName ] = [];  // create an array for the listeners
        }

        var listener = {
            fn: fn,
            scope: scope || window   // default to firing events in the window scope, if the scope arg is not provided
        };
        this.events[ eventName ].push( listener );
    },

	/**
     * 移除监听
     * @method removeListener
     * @param {String} eventName 事件名称
	 * @param {Function} fn 回调
	 * @param {Object} scope 作用域
     */
    removeListener : function( eventName, fn, scope ) {
        var listeners = this.events[ eventName ],
            i, len;

        // No subscribers, simply return
        if( !listeners || listeners.length === 0 ) {
            return;
        }

        scope = scope || window;
        for( i = 0, len = listeners.length; i < len; i++ ) {
            if( listeners[ i ].fn === fn && listeners[ i ].scope === scope ) {
                listeners.splice( i, 1 );  // remove the listener entry
                break;
            }
        }
    },

	/**
     * 释放事件
     * @method fireEvent
     * @param {String} eventName 事件名称
	 * @param {Function} fn 回调
	 * @param {Object} scope 作用域
     */
    fireEvent : function( eventName /* all other args are provided to the listeners' handler functions */ ) {
        var listeners = this.events[ eventName ],
            i, len;

        // No subscribed listeners, simply return
        if( !listeners || listeners.length === 0 ) {
            return;
        }

        var eventArgs = Array.prototype.slice.call( arguments, 1 );  // grab all args provided to fireEvent except the event name, to provide to handlers
        for( i = 0, len = listeners.length; i < len; i++ ) {
            listeners[ i ].fn.apply( listeners[ i ].scope, eventArgs );
        }
    }

} );
/**
 * 地理位置转换
 * @class PlaceSearch
 */
HQMap.Geocoder = Class.create( {

	/**
	 * 逆地理位置编码
	 * @param {String} location 位置信息
	 * @param {Function} callback 回调函数
	 * @param {Object} geocoderOptions 配置信息
	 * geocoderOptions.simple true 表示返回普通地址如：xx市xx路
	 * @method getAddress
	 */
	getSimpleAddress: function( location, callback, geocoderOptions ) {
		var me = this;
		var fn = function(info){
			var address = "";
			if(info){
				var c = info.addressComponent;
				if(c){
					address += c.province + c.city;
					if(c.district){
						address += c.district;
					}
					if(c.township){
						address += c.township;
					}
				}
				if(info.roads && info.roads.length > 0){
					address += info.roads[0].name;
				}

				if(geocoderOptions && geocoderOptions.simple){
					// 返回简单地址 xx市xx路
				}else{
					if(info.pois && info.pois.length > 0){
						var poi = info.pois[0];
						var direction = "";
						switch(poi.direction){
							case '北':
								direction = '南';
							break;
							case '南':
								direction = '北';
							break;
							case '西':
								direction = '东';
							break;
							case '东':
								direction = '西';
							break;
							case '西南':
								direction = '东北';
							break;
							case '东北':
								direction = '西南';
							break;
							case '东南':
								direction = '西北';
							break;
							case '西北':
								direction = '东南';
							break;
							default:
								'';
						}
						address += poi.name + direction + Math.round(poi.distance) + '米';
					}
				}
			}else{
				address = "";
			}
			if(callback){
				callback.call( me, address,geocoderOptions);
			}
		}
		this.getAddress(location,fn,$.extend({extensions:'all'},geocoderOptions||{}));
	},

	/**
	 * 地理位置编码
	 * @method getLocation
	 * @param {string} address 地址
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {Object} geocoderOptions 地理编码参数
	 * @param {string} geocoderOptions.city 城市，地理编码时，设置地址描述所在城市 可选值：城市名（中文或中文全拼）、citycode、adcode；默认值：“全国”
	 */
	getLocation: function( address, callback, geocoderOptions ) {
		var me = this;
		this._getGeocoder( geocoderOptions, function( geocoder ) {
			geocoder.getLocation( address, function( status, result ) {
				var data = {};
				if( status === "complete" ) {
					data = me._getGeoResult( result.geocodes );
				}else if ( status === "no_data" ){
					data.message = "0051"; // no data
				}else{
					data.message = "0052"; // no reason
				}
				if( callback ) {
					callback.call( me, data );
				}
			} );
		} );
	},


	/**
	 * 逆地理编码
	 * @method getAddress
	 * @param {string} address 地址
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {Object} geocoderOptions 逆地理编码参数
	 * @param {number} geocoderOptions.radius 逆地理编码时，以给定坐标为中心点，单位：米 取值范围：0-3000 默认值：1000
	 * @param {string} geocoderOptions.extensions 逆地理编码时，返回信息详略 默认值：base，返回基本地址信息；取值为：all，返回地址信息及附近poi、道路、道路交叉口等信息
	 */
	getAddress: function( location, callback, geocoderOptions ) {
		var me = this;
		if(geocoderOptions && geocoderOptions.correct){
			this.correctLocation(location.toString(),function(locations){
				this._getGeocoder( geocoderOptions, function( geocoder ) {
					var l = locations.split(",");
					l = new HQMap.LngLat( l[0], l[1] );
					geocoder.getAddress( l._, function( status, result ) {
						var obj = null;
						if( status === "complete" ) {
							obj = me._getReGeoResult( result.regeocode );
						}
						if( callback ) {
							callback.call( me, obj );
						}
					} );
				} );
			});
		}else{
			this._getGeocoder( geocoderOptions, function( geocoder ) {
				if( $.isArray( location ) ) {
					location = new HQMap.LngLat( location[0], location[1] );
				}
				geocoder.getAddress( location._, function( status, result ) {
					var obj = null;
					if( status === "complete" ) {
						obj = me._getReGeoResult( result.regeocode );
					}
					if( callback ) {
						callback.call( me, obj );
					}
				} );
			} );
		}
	},

	/**
	 * 原始经纬度纠偏
	 * @method correctLocation
	 * @param {string} location 原始经纬度 如：116.481499,39.990475
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {options} 纠偏参数
	 */
	correctLocation: function(location,callback,options){
		var me = this;
		$.ajax({
			url: "http://restapi.amap.com/v3/assistant/coordinate/convert",
			data: "locations="+location+"&coordsys=gps&output=json&key="+me.key,
			dataType: 'jsonp',
			success: function(msg){
		     	if(msg && msg.info === "ok"){
		     		if( callback ) {
			     		callback.call(me, msg.locations);
					}
		     	}
		   	}
		});
	},

	_getGeoResult: function( geocodes ) {
		var me = this;
		return $.map( geocodes, function( geocode ) {
			var info = {
				formattedAddress : geocode.formattedAddress,
				adcode : geocode.adcode,
				level : geocode.level
			};

			if ( geocode.location ) {
				info.location = new HQMap.LngLat( geocode.location.getLng(), geocode.location.getLat() );
			}

			if( geocode.addressComponent ) {
				info.addressComponent = me._getAddressComponent( geocode.addressComponent );
			}

			return info;
		} );
	},

	_getAddressComponent : function( addressComponent ) {
		var info = {
			province : addressComponent.province,
			city : addressComponent.city,
			citycode : addressComponent.citycode,
			district : addressComponent.district,
			adcode : addressComponent.adcode,
			township : addressComponent.township,
			street : addressComponent.street,
			streetNumber : addressComponent.streetNumber,
			neighborhood : addressComponent.neighborhood,
			neighborhoodType : addressComponent.neighborhoodType,
			building : addressComponent.building,
			buildingType : addressComponent.buildingType
		};
		if ( addressComponent.businessAreas ) {
			info.businessAreas = $.map( addressComponent.businessAreas, function( businessArea ) {
				var info = {
					id : businessArea.id,
					name : businessArea.name
				};
				if( businessArea.location ) {
					info.location = new HQMap.LngLat( businessArea.location.getLng(), businessArea.location.getLat() );
				}
				return info;
			} );
		}
		return info;
	},


	_getReGeoResult: function( regeocode ) {
		var info = {
			formattedAddress : regeocode.formattedAddress
		};

		if( regeocode.addressComponent ) {
			info.addressComponent = this._getAddressComponent( regeocode.addressComponent );
		}

		if( regeocode.roads ) {
			info.roads = $.map( regeocode.roads, function( road ) {
				var info = {
					id : road.id,
					name : road.name,
					distance : road.distance,
					direction : road.direction
				};
				if( road.location ) {
					info.location = new HQMap.LngLat( road.location.getLng(), road.location.getLat() );
				}
				return info;
			} );
		}

		if( regeocode.crosses ) {
			info.crosses = $.map( regeocode.crosses, function( cross ) {
				var info = {
					distance : cross.distance,
					direction : cross.direction,
					first_id : cross.first_id,
					first_name : cross.first_name,
					second_id : cross.second_id,
					second_name : cross.second_name
				};
				if( cross.location ) {
					info.location = new HQMap.LngLat( cross.location.getLng(), cross.location.getLat() );
				}
				return info;
			} );
		}

		if( regeocode.pois ) {
			info.pois = $.map( regeocode.pois, function( poi ) {
				var info = {
					id : poi.id,
					name : poi.name,
					type : poi.type,
					tel : poi.tel,
					distance : poi.distance,
					direction : poi.direction,
					address : poi.address,
					businessArea : poi.businessArea
				};
				if( poi.location ) {
					info.location = new HQMap.LngLat( poi.location.getLng(), poi.location.getLat() );
				}
				return info;
			} );
		}

		return info;
	},

	/**
	 * 获取地理编码工具
	 */
	_getGeocoder : function( geocoderOptions, callback ) {
		var me = this;
        AMap.service("AMap.Geocoder", function() {
        	if( geocoderOptions ) {
        		me._geocoder = new AMap.Geocoder( geocoderOptions );
        	} else {
        		me._geocoder = new AMap.Geocoder();
        	}

        	if( callback ) {
        		callback.call( me, me._geocoder );
        	}
        });
	},

	/**
	 * 获取地理编码工具
	 */
	_getPlaceSearch : function( placeSearchOptions, callback ) {
		var me = this;
        AMap.service("AMap.PlaceSearch", function() {
        	if( placeSearchOptions ) {
        		me._placeSearch = new AMap.PlaceSearch( placeSearchOptions );
        	} else {
        		me._placeSearch = new AMap.PlaceSearch();
        	}

        	if( callback ) {
        		callback.call( me, me._placeSearch );
        	}
        });
	},

	/**
	 * 根据关键字搜索
	 * @param {String} keyword 关键字
	 * @param {Function} callback 回调函数
	 * @param {Object} placeSearchOptions 配置信息
	 * @method search
	 */
	search : function( keyword, callback, placeSearchOptions ){
		var me = this;
		this._getPlaceSearch( placeSearchOptions, function( placeSearch ) {

			placeSearch.search(keyword,function(status , result){
				 /**
				  * 关键字支持中文|中文全拼、繁体、英文当status为complete时，result为SearchResult；
		 		  * 当status为error时，result为错误信息info；
		 		  * 当status为no_data时，代表检索返回0结果
		 		  */
				var data = {};
				if( status === "complete" ) {
					data = me._getPlaceSearchResult( result.poiList.pois );
				}else if ( status === "no_data" ){
					data.message = "0051"; // no data
				}else{
					data.message = "0052"; // no reason
				}
				if( callback ) {
					callback.call( me, data );
				}
			});
		});
	},

	/**
	 * 根据中心点经纬度、半径以及关键字进行周边查询
	 * @param {String} keyword 关键字
	 * @param {HQMap.LngLat} center 中心点经纬度
	 * @param {Number} radius 半径 取值范围：0-50000
	 * @param {Function(List)} callback 回调函数
	 * @param {Object} placeSearchOptions 配置信息 如：{map : mapObj , type :  '餐饮|酒店'}.mapObj 为HQMap.Map对象或其子类HQMap.CommonMap对象
	 * 多个type中间用 “|”分隔
	 * @method searchNearBy
	 */
	searchNearBy : function( keyword,center,radius,callback, placeSearchOptions ){
		var me = this;
		this._getPlaceSearch( placeSearchOptions, function( placeSearch ) {
			if ($.isArray(center)) {
			    center = new HQMap.LngLat(center[0], center[1]);
		    }
			placeSearch.searchNearBy(keyword,center._,radius,function(status , result){
				var data = {};
				if( status === "complete" ) {
					data = me._getPlaceSearchResult( result.poiList.pois );
				}else if ( status === "no_data" ){
					data.message = "0051"; // no data
				}else{
					data.message = "0052"; // no reason
				}
				if( callback ) {
					callback.call( me, data );
				}
			});
		});
	},

	_getPlaceSearchResult: function( pois ) {
		var me = this;
		return $.map( pois, function( poi ) {
			var info = {
				formattedAddress : poi.address
			};

			if ( poi.location ) {
				info.location = new HQMap.LngLat( poi.location.getLng(), poi.location.getLat() );
			}
			return info;
		} );
	}

} );


/**
 * 组件
 * @class Component
 * @module HQMap
 * @namespace HQMap
 */
HQMap.Component = HQMap.Observable.extend( {


	/**
	 * 构造函数
	 *
	 * @constructor
	 */
    constructor : function() {
    	this._super();

		if( this.constructor.event ) {
			var me = this;
			$.each( this.constructor.event, function( eventVarName, eventName ) {
				if( eventName.indexOf("HQMap") === -1 ) {
					AMap.event.addListener( me._, eventName, function( event ) {
						this.trigger( eventName, this._getEventObject( event ) );
					}, me );
				}
			} );
		}
    },


    /**
     * 设置配置
     * @method set
     * @param {Object} options 配置信息
     */
     set : function( options ) {
    	if( options ) {
    		var me = this;
    		$.each( options, function( optionName, optionValue ) {
    			var methodName = "set" + HQMap.StringUtil.toFirstLetterUpperCase( optionName );
    			if( me[ methodName ] ) {
    				try{
	    				if(optionValue){
    						me[ methodName ]( optionValue );
    					}
				    }catch(e){
				       if(window.console){
				       		console.error(methodName + "(" + optionValue+") error : "+e.message);
				       }
				    }
    			}
    		});
    	}
    },


    /**
     * 添加事件监听
     * @method on
     * @param {string} eventName 事件名称
     * @param {function} fn 事件处理函数
     * @param {object} scope 事件处理函数的作用于
     */
    on : function( eventName, fn, scope ) {
    	// 高德组件编辑插件监听
    	if(eventName.toLowerCase().indexOf("edt_") == 0){
    		eventName = eventName.substr("edt_".length);
    		if( this._editer ){
    			var me = this;
    			AMap.event.addListener(this._editer,eventName,function(event){
    				fn(me._getEventObject(event));
    			});
    		}
    	}else{
	    	this.addListener.apply( this, arguments );
    	}
    },

    /**
     * 删除事件监听
     * @method off
     * @param {string} eventName - 事件名称
     * @param {function} fn - 事件处理函数
     * @param {object} scope - 事件处理函数的作用于
     */
    off : function( eventName, fn, scope ) {
    	// TODO 编辑插件监听删除，暂不支持
	    this.removeListener.apply( this, arguments );
    },


    /**
     * 触发指定事件
     * @method trigger
     * @param {string} eventName - 事件名称
     */
    trigger : function( eventName ) {
    	this.fireEvent.apply( this, arguments );
    },


    /**
     * 获取事件对象
     * @method _getEventObject
     * @return {HQMap.EventObject} 事件对象
     */
    _getEventObject: function( event ) {
    	if(event.target){
    		this._ = event.target;
    	}
		var eventOptions = {
			type: event.type,
			target: this
		};
		if( this.id ){
			eventOptions.overlayId = this.id;
		}
		if( event.lnglat ) {
			eventOptions.lnglat = new HQMap.LngLat( event.lnglat.lng, event.lnglat.lat );
		}
		if( event.pixel ) {
			eventOptions.pixel = new HQMap.Pixel( event.pixel.x, event.pixel.y );
		}
    	return new HQMap.EventObject( eventOptions );
    }


} );
/**
 * 覆盖物
 * @class Overlay
 * @module HQMap
 * @namespace HQMap
 */
HQMap.Overlay = HQMap.Component.extend({

    statics : {
	    count : 0
    },

    inheritedStatics : {
	    event : {
	        CLICK : "click",
	        DBLCLICK : "dblclick",
	        RIGHTCLICK : "rightclick",
	        MOUSEMOVE : "mousemove",
	        MOUSEDOWN : "mousedown",
	        MOUSEUP : "mouseup",
	        MOUSEOVER : "mouseover",
	        MOUSEOUT : "mouseout",
	        DRAGSTART : "dragstart",
	        DRAGGING : "dragging",
	        DRAGEND : "dragend",
	        SHOW : "show",
	        HIDE : "hide"
	    }
    },

    /**
	 * 编号
	 *
	 * @property id
	 * @type String
	 */
    id : null,

    /**
	 * 地图对象
	 *
	 * @property map
	 * @type HQMap.Map
	 */
    map : null,

    /**
     * 层叠顺序
	 * @property zIndex
	 * @type Number
	 */
    zIndex : 10,

    /**
     * 设置是否可拖拽移动
	 * @property draggable
	 * @type Boolean
	 */
    draggable : false,

    /**
     * 是否可点击
	 * @property clickable
	 * @type Boolean
	 */
    clickable : true,

    /**
     * 是否显示
	 * @property visible
	 * @type Boolean
	 */
    visible : true,

    /**
     * 显示级别
	 * @property visible
	 * @type Number [3-18] 0表示一直显示
	 */
    level : 0,

    /**
     * 事件是否冒泡
	 * @property bubble
	 * @type Boolean
	 */
    bubble : false,

     /**
     * 清空地图覆盖物时是否一直显示
	 * @property showAlways
	 * @type Boolean
	 */
    showAlways : false,

    /**
	 * 构造函数
	 * @param {Object} options 配置项
	 */
    constructor : function(options) {
	    this._super(arguments);

	    if (!this.id) {
		    this.id = "HQMap.Overlay_obj_" + HQMap.Overlay.count++;
	    }
	    if (this.map) {
		    this.map.addOverlay(this);
	    }
    },

    /**
	 * 获取编号
	 * @method getId
	 * @return String
	 */
    getId : function() {
	    return this.id;
    },

    /**
	 * 设置地图对象
	 * @method setMap
	 * @param {HQMap.Map} map 鸿泉地图实例
	 */
    setMap : function(map) {
	    this.map = map;
	    if (this.map) {
	    	// 清空地图覆盖物时，需要一直显示的加入 showAlways集合中
	    	if(this.showAlways){
	    		 this.map.getShowAlways().put(this.getId(), this);
	    	}else{
			    this.map.getOverlay().put(this.getId(), this);
	    	}
	    }
	    this._.setMap(map ? map._ : null);
    },

    /**
	 * 设置叠加顺序，默认最先添加的在最底层
	 * @method setZIndex
	 * @param {Number} zIndex 层叠值
	 */
    setZIndex : function(zIndex) {
	    this.zIndex = zIndex;

	    if (this._.setzIndex) {
		    this._.setzIndex(zIndex);
	    } else {
		    if (this._.setOptions) {
			    this._.setOptions({
				    zIndex : zIndex
			    });
		    }
	    }
    },

    /**
	 * 设置是否可拖拽移动
	 * @method setDraggable
	 * @param {Boolean} draggable 是否可拖拽
	 */
    setDraggable : function(draggable) {
	    this.draggable = draggable;

	    if (this._.setDraggable) {
		    this._.setDraggable(this.draggable);
	    } else {
		    if (this._.setOptions) {
			    this._.setOptions({
				    draggable : this.draggable
			    });
		    }
	    }
    },

    /**
	 * 设置是否可点击
	 * @method setClickable
	 * @param {Boolean} clickable 是否可点击
	 */
    setClickable : function(clickable) {
	    this.clickable = clickable;

	    if (this._.setClickable) {
		    this._.setClickable(this.clickable);
	    } else {
		    if (this._.setOptions) {
			    this._.setOptions({
				    clickable : this.clickable
			    });
		    }
	    }
    },

    /**
	 * 设置事件是否冒泡
	 * @method setBubble
	 * @param {Boolean} bubble 是否冒泡
	 */
    setBubble : function(bubble) {
	    this.bubble = bubble;

	    if (this._.setOptions) {
		    this._.setOptions({
			    bubble : this.bubble
		    });
	    }
    },

    /**
	 * 设置覆盖物显示级别
	 * @method setLevel
	 * @param {Number} level 显示级别
	 */
    setLevel : function(level) {
	    this.level = level;
    },

    /**
	 * 获取编号
	 * @method getLevel
	 * @return Number
	 */
    getLevel : function() {
	    return this.level;
    },

    /**
	 * 显示
	 * @method show
	 */
    show : function() {
	    this._.show();
    },

    /**
	 * 隐藏
	 * @method hide
	 */
    hide : function() {
	    this._.hide();
    }

});
/**
 * 线性覆盖物
 * @class LineOverlay
 * @module HQMap
 * @namespace HQMap
 */
HQMap.LineOverlay = HQMap.Overlay.extend( {


	/**
	 * 线条颜色，使用16进制颜色代码赋值
	 * @property strokeColor
	 * @type String
	 */
	strokeColor: "#006600",


	/**
	 * 线条透明度，取值范围[0,1]，0表示完全透明，1表示不透明
	 * @property strokeOpacity
	 * @type Float
	 */
	strokeOpacity: 0.9,


	/**
	 * 线条宽度
	 * @property strokeWeight
	 * @type Number
	 */
	strokeWeight: 3,


	/**
	 * 线条样式，实线:solid，虚线:dashed
	 * @property strokeStyle
	 * @type String
	 */
	strokeStyle: "solid",

	/**
	 * 线条是否带描边 默认false
	 * @property isOutline
	 * @type Boolean
	 */
	isOutline: false,

	/**
	 * 线条描边颜色 默认：#000000
	 * @property outlineColor
	 * @type String
	 */
	outlineColor: "#006600",

	/**
	 * 设置线条颜色
	 * @method setStrokeColor
	 * @param {String} strokeColor 颜色
	 */
	setStrokeColor : function( strokeColor ) {
		this.strokeColor = strokeColor;
		this._.setOptions( { strokeColor : strokeColor } );
	},


	/**
	 * 获取线条颜色
	 * @method getStrokeColor
	 * @return {String}
	 */
	getStrokeColor : function() {
		this.strokeColor = this._.getOptions().strokeColor;
		return this.strokeColor;
	},


	/**
	 * 设置线条透明度
	 * @method setStrokeOpacity
	 * @param {string} strokeOpacity 透明度
	 */
	setStrokeOpacity : function( strokeOpacity ) {
		this.strokeOpacity = strokeOpacity;
		this._.setOptions( { strokeOpacity : strokeOpacity } );
	},


	/**
	 * 获取线条透明度
	 * @method getStrokeOpacity
	 * @return {String}
	 */
	getStrokeOpacity : function() {
		this.strokeOpacity = this._.getOptions().strokeOpacity;
		return this.strokeOpacity;
	},


	/**
	 * 设置线条宽度
	 * @method setStrokeWeight
	 * @param {String} strokeWeight 宽度
	 */
	setStrokeWeight : function( strokeWeight ) {
		this.strokeWeight = strokeWeight;
		this._.setOptions( { strokeWeight : strokeWeight } );
	},


	/**
	 * 获取线条宽度
	 * @method getStrokeWeight
	 * @return {String}
	 */
	getStrokeWeight : function() {
		this.strokeWeight = this._.getOptions().strokeWeight;
		return this.strokeWeight;
	},


	/**
	 * 设置线条样式
	 * @method setStrokeStyle
	 * @param {String} strokeStyle 样式值
	 */
	setStrokeStyle : function( strokeStyle ) {
		this.strokeStyle = strokeStyle;
		this._.setOptions( { strokeStyle : strokeStyle } );
	},


	/**
	 * 获取线条样式
	 * @method getStrokeStyle
	 * @return {String}
	 */
	getStrokeStyle : function() {
		this.strokeStyle = this._.getOptions().strokeStyle;
		return this.strokeStyle;
	},

	/**
	 * 设置线条是否带描边
	 * @method setIsOutline
	 * @param {Boolean} strokeStyle 样式值
	 */
	setIsOutline : function( isOutline ) {
		this.isOutline = isOutline;
		this._.setOptions( { isOutline : isOutline } );
	},


	/**
	 * 获取线条是否带描边
	 * @method getIsOutline
	 * @return {Boolean}
	 */
	getIsOutline : function() {
		this.isOutline = this._.getOptions().isOutline;
		return this.isOutline;
	},

	/**
	 * 设置线条描边颜色
	 * @method setOutlineColor
	 * @param {Boolean} strokeStyle 样式值
	 */
	setOutlineColor : function( outlineColor ) {
		this.outlineColor = outlineColor;
		this._.setOptions( { outlineColor : outlineColor } );
	},


	/**
	 * 获取线条描边颜色
	 * @method getOutlineColor
	 * @return {Boolean}
	 */
	getOutlineColor : function() {
		this.outlineColor = this._.getOptions().outlineColor;
		return this.outlineColor;
	},


	/**
	 * 获取区域范围
	 * @method getBounds
	 * @return {HQMap.Bounds}
	 */
	getBounds : function() {
		var bounds = this._.getBounds();
		if( !bounds ) {
			return null;
		}
		var southWest = bounds.getSouthWest();
		var northEast = bounds.getNorthEast();
		return new HQMap.Bounds(
			new HQMap.LngLat( southWest.getLng(), southWest.getLat() ),
			new HQMap.LngLat( northEast.getLng(), northEast.getLat() )
		);
	}


} );
/**
 * 区域覆盖物
 * @module HQMap
 * @class AreaOverlay
 * @namespace HQMap
 */
HQMap.AreaOverlay = HQMap.LineOverlay.extend( {


	/**
	 * 圆形填充颜色,使用16进制颜色代码赋值
	 * @property fillColor
	 * @type String
	 */
	fillColor: "#006600",


	/**
	 * 圆形填充透明度，取值范围[0,1]，0表示完全透明，1表示不透明
	 * @property fillOpacity
	 * @type Float
	 * @default 0.9
	 */
	fillOpacity: 0.9,


	/**
	 * 设置填充色
	 * @method setFillColor
	 * @param {string} fillColor 填充颜色
	 */
	setFillColor : function( fillColor ) {
		this.fillColor = fillColor;
		this._.setOptions( { fillColor : fillColor } );
	},


	/**
	 * 获取填充色
	 * @method getFillColor
	 * @return {String}
	 */
	getFillColor : function() {
		this.fillColor = this._.getOptions().fillColor;
		return this.fillColor;
	},


	/**
	 * 设置填充色透明度
	 * @method setFillOpacity
	 * @param {Number} fillOpacity 透明度
	 */
	setFillOpacity : function( fillOpacity ) {
		this.fillOpacity = fillOpacity;
		this._.setOptions( { fillOpacity : fillOpacity } );
	},


	/**
	 * 获取填充色透明度
	 * @method getFillOpacity
	 * @return Number
	 */
	getFillOpacity : function() {
		this.fillOpacity = this._.getOptions().fillOpacity;
		return this.fillOpacity;
	},


	/**
	 * 判断指定点是否包含在区域范围内
	 * @method contains
	 * @param {HQMap.LngLat} point 点位信息
	 */
	contains : function( point ) {
		return this._.contains( point._ );
	}


} );
/**
 * 圆
 * @class Circle
 */
HQMap.Circle = HQMap.AreaOverlay.extend( {


	/**
	 * 圆心位置
	 * @property center
	 * @type HQMap.LngLat
	 */
	center: null,


	/**
	 * 圆半径，单位:米
	 * @property radius
	 * @type Number
	 */
	radius: 10000,


	/**
	 * 是否可编辑
	 * @property editable
	 * @type Boolean
	 */
	editable: false,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {Object} options 配置选项
	 */
    constructor : function( options, _ ) {
    	$.extend( this, options || {} );
    	this._ = _ ? _ : new AMap.Circle( this._getOptions() );
    	this._super( arguments );

    	this.set( this._getCustomOptions() );
    },


    /**
     * 设置中心点
     * @method setCenter
     * @param {HQMap.LngLat} center 中心点坐标
     */
	setCenter : function( center ) {
		if( $.isArray( center ) ) {
			this.center = new HQMap.LngLat( center[ 0 ], center[ 1 ] );
		} else {
			this.center = center;
		}
		this._.setCenter( this.center._ );
	},


	/**
	 * 获取中心点
	 * @method getCenter
	 * @return HQMap.LngLat
	 */
	getCenter: function() {
		var center = this._.getCenter();
		if( this.center ) {
			this.center._ = center;
		} else {
			this.center = new HQMap.LngLat( center.getLng(), center.getLat() );
		}
		return this.center;
	},


	/**
	 * 设置半径
	 * @method setRadius
	 * @param {Number} radius 半径
	 *
	 */
	setRadius : function( radius ) {
		this.radius = radius;
		this._.setRadius( radius );
	},


	/**
	 * 获取半径
	 * @method getRadius
	 * @return Number
	 */
	getRadius : function() {
		this.radius = this._.getRadius();
		return this.radius;
	},


	/**
	 * 设置是否可编辑
	 * @method setEditable
	 * @param {Boolean} editable 是否可编辑
	 */
	setEditable : function( editable ) {
		this.editable = editable;

		if( this._editer ) {
			if( this._editer ) {
				if( this.editable ) {
					this._editer.open();
				} else {
					this._editer.close();
				}
			}
		} else {
			if( this.map ) {
				var me = this;
				this.map._.plugin( "AMap.CircleEditor", function() {
					me._editer = new AMap.CircleEditor( me.map._, me._ );
					if( me._editer ) {
						if( me.editable ) {
							me._editer.open();
						} else {
							me._editer.close();
						}
					}
				} );
			}
		}
	},



	/**
	 * 获取自定义配置信息
	 * @method _getCustomOptions
	 * @private
	 * @return Object
	 */
	_getCustomOptions : function() {
		var customOptions = {
			editable : this.editable
		};
		return customOptions;
	},


	/**
	 * 获取配置信息
	 * @method _getOptions
	 * @private
	 * @return Object
	 */
	_getOptions : function() {
		var circleOptions = {
			radius : this.radius,
			zIndex : this.zIndex,
			draggable : this.draggable,
			clickable : this.clickable,
			visible : this.visible,
			bubble : this.bubble,
			strokeColor : this.strokeColor,
			strokeOpacity : this.strokeOpacity,
			strokeWeight : this.strokeWeight,
			strokeStyle : this.strokeStyle,
			fillColor : this.fillColor,
			fillOpacity : this.fillOpacity
		};
		if( this.map ) {
			circleOptions.map = this.map._;
		}
		if( this.center ) {
			circleOptions.center = this.center._;
		}
		return circleOptions;
	}

} );

/**
 * 右键菜单
 * @class ContextMenu
 */
HQMap.ContextMenu = HQMap.Component.extend( {


    statics : {
    	event : {
    		OPEN : "open",
    		CLOSE : "close"
    	}
    },


	/**
	 * 右键菜单显示的位置
	 * @property position
	 * @type HQMap.LngLat
	 */
	position : null,


	/**
	 * 右键菜单内容（针对自定义菜单时，添加菜单内容及功能。可以是HTML要素字符串或者HTML DOM对象。）
	 * @property content
	 * @type Object
	 */
	content : null,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {object} options 配置选项
	 */
    constructor : function( options ) {
    	$.extend( this, options || {} );
    	this._ = new AMap.ContextMenu( this._getOptions() );
    	this._super();
    },


	/**
	 * 添加菜单项
	 * @method addItem
	 * @param {string} text 菜单显示内容
	 * @param {function} fn 该菜单下需进行的操作
	 * @param {number} num 当前菜单项在右键菜单中的排序位置，以0开始
	 */
	addItem : function( text, fn, num ) {
		this._.addItem( text, fn, num );
	},


	/**
	 * 删除菜单项
	 * @method removeItem
	 * @param {string} text 菜单名称
	 * @param {function} fn 菜单处理函数
	 */
	removeItem : function( text, fn ) {
		this._.removeItem( text, fn );
	},


	/**
	 * 打开
	 * @method open
	 * @param {HQMap.Map} map 地图实例
	 * @param {HQMap.LngLat} position 点位信息
	 */
	open : function( map, position ) {
		this._.open( map._, position._ );
	},


	/**
	 * 关闭
	 * @method close
	 */
	close : function() {
		this._.close();
	},


	/**
	 * 获取右键菜单的配置信息
	 * @method _getOptions
	 * @return {object}
	 */
	_getOptions : function() {
		var circleOptions = {};
		if( this.content ) {
			circleOptions.content = this.content;
		}
		if( this.position ) {
			if( $.isArray( this.position ) ) {
				this.position = new HQMap.LngLat( this.position[ 0 ], this.position[ 1 ] );
			}
			circleOptions.position = this.position._;
		}
		return circleOptions;
	}


} );
/**
 * 信息窗
 * @class InfoWindow
 */
HQMap.InfoWindow = HQMap.Component.extend( {


    statics : {
    	event : {
    		OPEN : "open",
    		CLOSE : "close"
    	}
    },


	/**
	 * 是否自定义窗体。设为true时，信息窗体外框及内容完全按照content所设的值添加（默认为false，即在系统默认的信息窗体外框中显示content内容）
	 * @property isCustom
	 * @type Boolean
	 * @default false
	 */
	isCustom : false,


	/**
	 * 是否自动调整窗体到视野内（当信息窗体超出视野范围时，通过该属性设置是否自动平移地图，使信息窗体完全显示）
	 * @property autoMove
	 * @type Boolean
	 * @default true
	 */
	autoMove : true,


	/**
	 * 控制是否在鼠标点击地图后关闭信息窗体，默认false，鼠标点击地图后不关闭信息窗体
	 * @property  closeWhenClickMap
	 * @type Boolean
	 * @default false
	 */
	closeWhenClickMap : false,


	/**
	 * 显示内容，可以是HTML要素字符串或者HTML DOM对象
	 * @property content
	 * @type Obeject
	 */
	content : null,


	/**
	 * 信息窗体尺寸（isCustom为true时，该属性无效）
	 * @property size
	 * @type HQMap.Size
	 */
	size : null,


	/**
	 * 相对于基点的偏移量。默认情况是信息窗体的底部中心点(BOTTOM_CENTER) 和基点之间的偏移量
	 * @property offset
	 * @type HQMap.Pixel
	 */
	offset : null,


	/**
	 * 信息窗体显示基点位置
	 * @property position
	 * @type HQMap.LngLat
	 */
	position : null,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {Object} options 配置选项
	 */
    constructor : function( options ) {
    	$.extend( this, options || {} );
    	this._ = new AMap.InfoWindow( this._getInfoWindowOptions() );
    	this._super();
    },


	/**
	 * 设置信息窗体内容，可通过该函数动态更新信息窗体中的信息
	 * @method setContent
	 * @param {(HTML|DOM)} content 内容信息
	 */
	setContent : function( content ) {
		this.content = content;
		this._.setContent( this.content );
	},


	/**
	 * 获取信息窗体内容 ，结果以字符串方式返回
	 * @method getContent
	 * @return String
	 */
	getContent : function() {
		this.content = this._.getContent();
		return this.content;
	},


	/**
	 * 设置显示基点位置
	 * @method setPosition
	 * @param {HQMap.LngLat} position 位置信息
	 */
	setPosition : function( position ) {
		if( $.isArray( position ) ) {
			this.position = new HQMap.LngLat( position[ 0 ], position[ 1 ] );
		} else {
			this.position = position;
		}
		this._.setPosition( this.position._ );
	},


	/**
	 * 获取显示基点位置
	 * @method getPosition
	 * @return HQMap.LngLat
	 */
	getPosition : function() {
		var position = this._.getPosition();
		if( position ) {
			this.position = new HQMap.LngLat( position.getLng(), position.getLat() );
		}
		return this.position;
	},


	/**
	 * 设置信息窗体大小（isCustom为false时有效）
	 * @method setSize
	 * @param {HQMap.Size} size 大小
	 */
	setSize : function( size ) {
		if( $.isArray( size ) ) {
			this.size = new HQMap.Size( size[ 0 ], size[ 1 ] );
		} else {
			this.size = size;
		}
		this._.setSize( this.size._ );
	},


	/**
	 * 获取信息窗体大小
	 * @method getSize
	 * @return HQMap.LngLat
	 */
	getSize : function() {
		var size = this._.getSize();
		if( size ) {
			this.size = new HQMap.Size( size.getLng(), size.getLat() );
		}
		return this.size;
	},


	/**
	 * 设置显示优先级
	 * @method setZIndex
	 * @param {number} zIndex 层级
	 */
	setZIndex : function( zIndex ) {
		this.zIndex = zIndex;
		this._.setOptions( { zIndex : zIndex } );
	},


	/**
	 * 设置偏移量
	 * @method setOffset
	 * @param {HQMap.Pixel} offset 偏移量
	 */
	setOffset : function( offset ) {
		if( $.isArray( offset ) ) {
			this.offset = new HQMap.Pixel( offset[ 0 ], offset[ 1 ] );
		} else {
			this.offset = offset;
		}
		this._.setOffset( this.offset );
	},


	/**
	 * 获取偏移量
	 * @method getOffset
	 * @return HQMap.Pixel
	 */
	getOffset : function() {
		var offset = this._.getOffset();
		if( offset ) {
			this.offset = new HQMap.Pixel( offset.getX(), offset.getY() );
		}
		return this.offset;
	},


	/**
	 * 获取信息窗体是否打开
	 * @method getIsOpen
	 * @return boolean
	 */
	getIsOpen : function() {
		return this._.getIsOpen();
	},


	/**
	 * 在地图的指定位置打开信息窗体
	 * @method open
	 * @param {HQMap.Map} map 地图实例
	 * @param {HQMap.LngLat} position 点位信息
	 */
	open : function( map, position ) {
		if( position ) {
			this.setPosition( position );
		}
		if ( this.position ) {
			this._.open( map._, position._ );
		}
	},


	/**
	 * 关闭
	 * @method
	 */
	close : function() {
		this._.close();
	},


	/**
	 * 获取信息窗的配置信息
	 * @method _getInfoWindowOptions
	 * @return {object}
	 */
	_getInfoWindowOptions : function() {
		var circleOptions = {
			isCustom : this.isCustom,
			autoMove : this.autoMove,
			closeWhenClickMap : this.closeWhenClickMap
		};
		if( this.content ) {
			circleOptions.content = this.content;
		}
		if( this.size ) {
			if( $.isArray( this.size ) ) {
				this.size = new HQMap.Size( this.size[ 0 ], this.size[ 1 ] );
			}
			circleOptions.size = this.size._;
		}
		if( this.offset ) {
			if( $.isArray( this.offset ) ) {
				this.offset = new HQMap.Pixel( this.offset[ 0 ], this.offset[ 1 ] );
			}
			circleOptions.offset = this.offset._;
		}
		if( this.position ) {
			if( $.isArray( this.position ) ) {
				this.position = new HQMap.LngLat( this.position[ 0 ], this.position[ 1 ] );
			}
			circleOptions.position = this.position._;
		}
		return circleOptions;
	}


} );
/**
 * 地图
 * @class Map
 */
HQMap.Map = HQMap.Component.extend( {


	inheritedStatics : {
    	event : {
    		COMPLETE : "complete",
    		RESIZE : "resize",
    		CLICK : "click",
    		DBLCLICK : "dblclick",
    		RIGHTCLICK : "rightclick",
    		MOUSEDOWN : "mousedown",
    		MOUSEUP : "mouseup",
    		MOUSEOVER : "mouseover",
    		MOUSEOUT : "mouseout",
    		MOUSEMOVE : "mousemove",
    		MOUSEWHEEL : "mousewheel",
    		MAPMOVE : "mapmove",
    		MOVESTART : "movestart",
    		MOVEEND : "moveend",
    		HOTSPOTCLICK : "hotspotclick",
    		HOTSPOTOVER : "hotspotover",
    		HOTSPOTOUT : "hotspotout",
    		ZOOMCHANGE : "zoomchange",
    		ZOOMSTART : "zoomstart",
    		ZOOMEND : "zoomend",
    		SHOW : "show",
    		HIDE : "hide",
    		DRAW_CIRCLE : "HQMap.drawcircle",
    		DRAW_MARKER : "HQMap.drawmarker",
    		DRAW_POLYLINE : "HQMap.drawpolyline",
    		DRAW_POLYGON : "HQMap.drawpolygon",
    		RANGE_POLYGON : "HQMap.drawpolygon",
    		RANGING_ADDNODE : "HQMap.ranging.addnode",
    		RANGING_REMOVENODE : "HQMap.ranging.removenode",
    		RANGING_END : "HQMap.ranging.end"
    	}
    },

	key : "461ffb5bb67254cd343aa8add090d53d",

	/**
	 * 需要显示地图的容器ID
	 * @property mapContainer
	 * @type String
	 */
	mapContainer : "",


	/**
	 * 中心点坐标值
	 * @property center
	 * @type HQMap.LngLat
	 */
	center : null,


	/**
	 * 缩放级别
	 * @property zoom
	 * @type Number
	 * @default 9
	 */
	zoom : 9,


	/**
	 * 语言类型
	 * @property lang
	 * @type String
	 * @default zh_cn
	 */
	lang : "zh_cn",


	/**
	 * 是否开启热
	 * @property isHotspot
	 * @type Boolean
	 * @default false
	 */
	isHotspot : false,


	/**
	 * 是否监控容器尺寸变化
	 * @property resizeEnable
	 * @type Boolean
	 * @default true
	 */
	resizeEnable : true,


	/**
	 * 是否可通过鼠标拖拽平移
	 * @property dragEnable
	 * @type Boolean
	 * @default true
	 */
	dragEnable : true,


	/**
	 * 是否可缩放
	 * @property zoomEnable
	 * @type Boolean
	 */
	zoomEnable : true,


	/**
	 * 是否可通过双击鼠标放大地图
	 * @property doubleClickZoom
	 * @type Boolean
	 * @default true
	 */
	doubleClickZoom : true,


	/**
	 * 是否可通过键盘控制，方向键控制地图平移，"+"和"-"可以控制地图的缩放，Ctrl+“→”顺时针旋转，Ctrl+“←”逆时针旋转
	 * @property keyboardEnable
	 * @type Boolean
	 * @default true
	 */
	keyboardEnable : true,


	/**
	 * 是否可通过鼠标滚轮缩放浏览
	 * @property scrollWheel
	 * @type Boolean
	 * @default true
	 */
	scrollWheel : true,


	/**
	 * 是否显示类型切换控件
	 * @property showMapType
	 * @type Boolean
	 * @default false
	 */
	showMapType : false,


	/**
	 * 是否显示鹰眼
	 * @property showOverview
	 * @type Boolean
	 * @default true
	 */
	showOverview : true,


	/**
	 * 是否显示比例尺控件
	 * @property showScale
	 * @type Boolean
	 * @default true
	 */
	showScale : true,


	/**
	 * 是否显示工具条控件
	 * @property showToolbar
	 * @type Boolean
	 * @default true
	 */
	showToolbar : true,


	/**
	 * 是否显示卫星图
	 * @property showSatellite
	 * @type Boolean
	 * @default false
	 */
	showSatellite : false,


	/**
	 * 是否显示路网
	 * @property showRoadNet
	 * @type Boolean
	 * @default false
	 */
	showRoadNet : false,


	/**
	 * 是否显示实时交通
	 * @property showTraffic
	 * @type Boolean
	 * @default false
	 */
	showTraffic : false,

	/**
	 * 是否显示聚合
	 * @property showCluster
	 * @type Boolean
	 * @default false
	 */
	 showCluster : false,

	/**
	 * 构造函数
	 * @method constructor
	 * @constructor
	 * @param {Object} options  配置选项
	 */
    constructor : function( mapContainer, options ) {
    	$.extend( this, options || {}, { mapContainer : mapContainer } );
    	this._ = new AMap.Map( mapContainer, this._getOptions() );
    	this._super();

    	// 不同覆盖物，如标注点、线路、区域等
    	this.overlays = new HQMap.HashMap();
    	// 清除覆盖物时，总是在地图上显示的覆盖物，如：测试插件，编辑插件创建的覆盖物
    	this.showAlwayss = new HQMap.HashMap();

    	this.set( this._getCustomOptions() );
    },


	/**
	 * 设置中心点
	 * @method setCenter
	 * @param {HQMap.LngLat} center  指定点坐标
	 */
	setCenter : function( center ) {
		if( $.isArray( center ) ) {
			this.center = new HQMap.LngLat( center[ 0 ], center[ 1 ] );
		} else {
			this.center = center;
		}
		this._.setCenter( this.center._ );
	},


	/**
	 * 获取中心点
	 * @method getCenter
	 * @return HQMap.LngLat 中心点坐标
	 */
	getCenter : function() {
		var center = this._.getCenter();
		this.center = new HQMap.LngLat( center.getLng(), center.getLat() );
		return this.center;
	},


	/**
	 * 设置缩放级别
	 * @method setZoom
	 * @param {String} zoom 缩放级别
	 */
	setZoom : function( zoom ) {
		this.zoom = zoom;
		this._.setZoom( this.zoom );
	},


	/**
	 * 获取缩放级别
	 * @method getZoom
	 * @return Number
	 */
	getZoom : function() {
		this.zoom = this._.getZoom();
		return this.zoom;
	},


	/**
	 * 地图缩放至指定级别并以指定点为地图显示中心点
	 * @method setZoomAndCenter
	 * @param {String} zoom 级别
	 * @param {String} center 中心点
	 */
	setZoomAndCenter : function( zoom, center ) {
		this.zoom = zoom;
		this.center = center;
		this._.setZoomAndCenter( this.zoom, this.center._ );
	},


	/**
	 * 设置语言类型，设置后底图重新加载
	 * @method setLang
	 * @param {string} lang 语言信息
	 */
	setLang : function( lang ) {
		this.lang = lang;
		this._.setLang( this.lang );
	},


	/**
	 * 获取底图语言类型
	 * @method getLang
	 * @return String
	 */
	getLang : function() {
		this.lang = this._.getLang();
		return this.lang;
	},


	setIsHotspot : function( isHotspot ) {
		this.isHotspot = isHotspot;
		this._.setStatus( { isHotspot: this.isHotspot } );
	},


	setResizeEnable : function( resizeEnable ) {
		this.resizeEnable = resizeEnable;
		this._.setStatus( { resizeEnable: this.resizeEnable } );
	},


	setDragEnable : function( dragEnable ) {
		this.dragEnable = dragEnable;
		this._.setStatus( { dragEnable: this.dragEnable } );
	},


	setZoomEnable : function( zoomEnable ) {
		this.zoomEnable = zoomEnable;
		this._.setStatus( { zoomEnable: this.zoomEnable } );
	},


	setDoubleClickZoom : function( doubleClickZoom ) {
		this.doubleClickZoom = doubleClickZoom;
		this._.setStatus( { doubleClickZoom: this.doubleClickZoom } );
	},


	setKeyboardEnable : function( keyboardEnable ) {
		this.keyboardEnable = keyboardEnable;
		this._.setStatus( { keyboardEnable: this.keyboardEnable } );
	},


	setScrollWheel : function( scrollWheel ) {
		this.scrollWheel = scrollWheel;
		this._.setStatus( { scrollWheel: this.scrollWheel } );
	},


	setShowMapType : function( showMapType ) {
		this.showMapType = showMapType;
		if( this._maptype ) {
			if( this.showMapType ) {
				this._maptype.show();
			} else {
				this._maptype.hide();
			}
		} else {
			var me = this;
			this._.plugin( "AMap.MapType", function() {
				me._maptype = new AMap.MapType();
				me._.addControl( me._maptype );
				if( me.showMapType ) {
					me._maptype.show();
				} else {
					me._maptype.hide();
				}
			});
		}
	},


	setShowOverview : function( showOverview ) {
		this.showOverview = showOverview;

		if( this._overview ) {
			if( this.showOverview ) {
				this._overview.show();
			} else {
				this._overview.hide();
			}
		} else {
			var me = this;
			this._.plugin( "AMap.OverView", function() {
				me._overview = new AMap.OverView();
				me._.addControl( me._overview );
				if( me.showOverview ) {
					me._overview.show();
				} else {
					me._overview.hide();
				}
			});
		}
	},


	setShowScale : function( showScale ) {
		this.showScale = showScale;

		if( this._scale ) {
			if( this.showScale ) {
				this._scale.show();
			} else {
				this._scale.hide();
			}
		} else {
			var me = this;
			this._.plugin( "AMap.Scale", function() {
				me._scale = new AMap.Scale();
				me._.addControl( me._scale );
				if( me.showScale ) {
					me._scale.show();
				} else {
					me._scale.hide();
				}
			});
		}
	},


	setShowToolbar : function( showToolbar ) {
		this.showToolbar = showToolbar;

		if( this._toolbar ) {
			if( this.showToolbar ) {
				this._toolbar.show();
			} else {
				this._toolbar.hide();
			}
		} else {
			var me = this;
			this._.plugin( "AMap.ToolBar", function() {
				me._toolbar = new AMap.ToolBar();
				me._.addControl( me._toolbar );
				if( me.showToolbar ) {
					me._toolbar.show();
				} else {
					me._toolbar.hide();
				}
			});
		}
	},


	setShowSatellite : function( showSatellite ) {
		this.showSatellite = showSatellite;
		this._.setLayers( this._getLayers() );
	},


	setShowRoadNet : function( showRoadNet ) {
		this.showRoadNet = showRoadNet;
		this._.setLayers( this._getLayers() );
	},


	setShowTraffic : function( showTraffic ) {
		this.showTraffic = showTraffic;
		this._.setLayers( this._getLayers() );

	},


	/**
	 * 按照行政区名称设置地图显示的中心点，行政区名称支持中国、省、市、区/县
	 * @method setCity
	 * @param {string} city 区域名
	 */
	setCity : function( city ) {
		this._.setCity( city );
	},


	/**
	 * 获取地图中心点所在区域，回调函数返回对象属性分别对应为{省，市，区/县} {province, city, citycode, district}
	 * @method getCity
	 * @param {function} callback 回调函数
	 * @return void
	 */
	getCity : function( callback ) {
		this._.getCity( function( result ) {
			var info = {
				city : result.city,
				citycode : result.citycode,
				district : result.district,
				province : result.province
			};
			callback.call( info, info );
		} );
	},


	/**
	 * 指定当前地图显示范围
	 * @method setBounds
	 * @param {HQMap.Bounds} bounds 指定的范围
	 */
	setBounds : function( bounds ) {
		this._.setBounds( bounds._ );
	},

	/**
	 * 获取当前地图视图范围
	 * @method getBounds
	 * @return HQMap.Bounds
	 */
	getBounds : function() {
		var bounds = this._.getBounds();
		if( !bounds ) {
			return null;
		}
		return new HQMap.Bounds(
			new HQMap.LngLat( bounds.southwest.getLng(), bounds.southwest.getLat() ),
			new HQMap.LngLat( bounds.northeast.getLng(), bounds.northeast.getLat() )
		);
	},


	/**
	 * 设置Map的限制区域，设定区域限制后，传入参数为限制的Bounds。地图仅在区域内可拖拽
	 * @method setLimitBounds
	 * @param {HQMap.Bounds} bounds 指定区域
	 */
	setLimitBounds : function( bounds ) {
		this._.setLimitBounds( bounds._ );
	},


	/**
	 * 获取Map的限制区域
	 * @method getLimitBounds
	 * @return HQMap.Bounds
	 */
	getLimitBounds : function() {
		var limitBounds = this._.getLimitBounds();
		if( !limitBounds ) {
			return null;
		}
		return new HQMap.Bounds(
			new HQMap.LngLat( limitBounds.southwest.getLng(), limitBounds.southwest.getLat() ),
			new HQMap.LngLat( limitBounds.northeast.getLng(), limitBounds.northeast.getLat() )
		);
	},


	/**
	 * 清除限制区域
	 * @method clearLimitBounds
	 */
	clearLimitBounds : function() {
		this._.clearLimitBounds();
	},


	/**
	 * 获取地图容器像素大小
	 * @method getSize
	 * @return HQMap.Size
	 */
	getSize : function() {
		var size = this._.getSize();
		return new HQMap.Size( size.getWidth(), size.getHeight() );
	},


	/**
	 * 地图放大一级显示
	 * @method zoomIn
	 */
	zoomIn : function() {
		this._.zoomIn();
	},


	/**
	 * 地图缩小一级显示
	 * @method zoomOut
	 */
	zoomOut : function() {
		this._.zoomOut();
	},


	/**
	 * 地图中心点平移至指定点位置
	 * @method panTo
	 * @param {HQMap.LngLat} positon 指定点
	 */
	panTo : function( positon ) {
		this._.panTo( positon._ );
	},


	/**
	 * 以像素为单位，沿x方向和y方向移动地图，x向右为正，y向下为正
	 * @method panBy
	 * @param {number} x x方向
	 * @param {number} y y方向
	 */
	panBy : function( x, y ) {
		this._.panBy( x, y );
	},


	/**
	 * 根据地图上添加的覆盖物分布情况，缩放平移地图到合适的视野级别
	 * @method setFitView
	 * @param {Array.<HQMap.Marker | HQMap.Polyline | HQMap.Polygon | HQMap.Circle | HQMap.InfoWindow>} overlayList 覆盖物图层 默认为当前地图上添加的所有覆盖物图层
	 */
	setFitView : function( overlayList ) {
		if(this.showCluster){
			// 聚合状态不适配地图显示
			return;
		}
		if( overlayList ) {
			this._.setFitView( $.map( overlayList, function( overlay ) {
				return overlay._;
			} ) );
		} else {
			this._.setFitView();
		}
	},


	/**
	 * 地图经纬度坐标转换为平面地图像素坐标
	 * @method pixelToLngLat
	 * @param {HQMap.Pixel} lnglat 像素坐标
	 * @param {number} zoom 缩放级别
	 * @return HQMap.LngLat
	 */
	pixelToLngLat : function( pixel, zoomLevel ) {
		var lnglat = this._.pixelToLngLat( pixel._, zoomLevel );
		return new HQMap.LngLat( lnglat.getLng(), lnglat.getLat() );
	},


	/**
	 * 地图经纬度坐标转换为平面地图像素坐标
	 * @method lnglatToPixel
	 * @param {HQMap.LngLat} lnglat 经纬度坐标
	 * @param {number} zoom 缩放级别
	 * @return HQMap.Pixel
	 */
	lnglatToPixel : function( lnglat, zoomLevel ) {
		var pixel = this._.lnglatToPixel( lnglat._, zoomLevel );
		return new HQMap.Pixel( pixel.getX(), pixel.getY() );
	},


	/**
	 * 地图容器像素坐标转为地图经纬度坐标
	 * @method containerToLngLat
	 * @param {HQMap.Pixel} lnglat 像素坐标
	 * @param {number} zoom 缩放级别
	 * @return HQMap.LngLat
	 */
	containerToLngLat : function( pixel, zoom ) {
		var lnglat = this._.containerToLngLat( pixel._, zoom );
		return new HQMap.LngLat( lnglat.getLng(), lnglat.getLat() );
	},


	/**
	 * 地图经纬度坐标转为地图容器像素坐标
	 * @method lngLatToContainer
	 * @param {HQMap.LngLat} lnglat 经纬度坐标
	 * @param {number} zoom 缩放级别
	 * @return HQMap.Pixel
	 */
	lngLatToContainer : function( lnglat, zoom ) {
		var pixel = this._.lngLatToContainer( lnglat._, zoom );
		return new HQMap.Pixel( pixel.getX(), pixel.getY() );
	},


	/**
	 * 设置是否开启拉框放大模式
	 * @method setRectZoomIn
	 * @param {boolean} enable 是否开启拉框放大模式
	 * @param {object} rectOptions 矩形的样式 可选
	 */
	setRectZoomIn : function( enable, rectOptions ) {
		this._setDrawOverlay( "rectZoomIn", enable, rectOptions, false, false );
	},


	/**
	 * 设置是否开启拉框缩小模式
	 * @method setRectZoomOut
	 * @param {boolean} enable 是否开启拉框缩小模式
	 * @param {object} rectOptions 矩形的样式 可选
	 */
	setRectZoomOut : function( enable, rectOptions ) {
		this._setDrawOverlay( "rectZoomOut", enable, rectOptions, false, false );
	},


	/**
	 * 设置是否开启鼠标画圆
	 * @method setDrawCircle
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项 可选
	 * @param {object} options.circleOptions 圆配置选项
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setDrawCircle : function( enable, options ) {
		if( options ) {
			this._setDrawOverlay( "circle", enable, options.circleOptions, !!options.once, !!options.clearOverlay );
		} else {
			this._setDrawOverlay( "circle", enable, null, false, false );
		}
	},


	/**
	 * 设置是否开启鼠标画标注点
	 * @method setDrawMarker
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项
	 * @param {object} options.markerOptions 标注点配置选项
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setDrawMarker : function( enable, options ) {
		if( options ) {
			this._setDrawOverlay( "marker", enable, options.markerOptions, !!options.once, !!options.clearOverlay );
		} else {
			this._setDrawOverlay( "marker", enable, null, false, false );
		}
	},


	/**
	 * 设置是否开启鼠标画折线
	 * @method setDrawPolyline
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项
	 * @param {object} options.polylineOptions 折线配置选项
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setDrawPolyline : function( enable, options ) {
		if( options ) {
			this._setDrawOverlay( "polyline", enable, options.polylineOptions, !!options.once, !!options.clearOverlay );
		} else {
			this._setDrawOverlay( "polyline", enable, null, false, false );
		}
	},


	/**
	 * 设置是否开启鼠标画多边形
	 * @method setDrawPolygon
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项
	 * @param {object} options.polygonOptions 多边形配置选项
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setDrawPolygon : function( enable, options ) {
		if( options ) {
			this._setDrawOverlay( "polygon", enable, options.polygonOptions, !!options.once, !!options.clearOverlay );
		} else {
			this._setDrawOverlay( "polygon", enable, null, false, false );
		}
	},


	/**
	 * 设置是否开启鼠标画矩形
	 * @method setDrawRectangle
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项
	 * @param {object} options.rectangleOptions 矩形配置选项
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setDrawRectangle : function( enable, options ) {
		if( options ) {
			this._setDrawOverlay( "rectangle", enable, options.rectangleOptions, !!options.once, !!options.clearOverlay );
		} else {
			this._setDrawOverlay( "rectangle", enable, null, false, false );
		}
	},


	/**
	 * 设置是否开启鼠标测距
	 * @method setRanging
	 * @param {boolean} enable 是否开启
	 * @param {Object} options 配置选项
	 * @param {Object} options.startMarkerOptions 设置量测起始点标记属性对象，包括点标记样式、大小等
	 * @param {Object} options.midMarkerOptions 设置量测中间点标记属性对象，包括点标记样式、大小等
	 * @param {Object} options.endMarkerOptions 设置量测结束点标记属性对象，包括点标记样式、大小等
	 * @param {Object} options.lineOptions 设置距离量测线的属性对象，包括线样式、颜色等
	 * @param {Object} options.tmpLineOptions 设置距离量测过程中临时量测线的属性对象，包括线样式、颜色等
	 * @param {String} options.startLabelText 设置量测起始点标签的文字内容
	 * @param {String} options.midLabelText 设置量测中间点处标签的文字内容
	 * @param {String} options.endLabelText 设置量测结束点处标签的文字内容
	 * @param {HQMap.Pixel} options.startLabelOffset 设置量测起始点标签的偏移量
	 * @param {HQMap.Pixel} options.midLabelOffset 设置量测中间点标签的偏移量
	 * @param {HQMap.Pixel} options.endLabelOffset 设置量测结束点标签的偏移量
	 * @param {boolean} options.once 只允许鼠标操作一次
	 * @param {boolean} options.clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	setRanging : function( enable, options ) {
		this._getRangingTool( options, function( rangingTool ){
			if( enable ) {
				rangingTool.turnOn();
				if( options && options.once ) {
					AMap.event.addListenerOnce( rangingTool, "end", function() {
						rangingTool.turnOff();
					});
				}
			} else {
				rangingTool.turnOff();
				if( options && options.clearOverlay ) {
					me.clearOverlay();
				}
			}
		} );
	},
	/**
	 * 设置是否开启面积量测功能
	 * @param {} enable 是否开启
	 * @param {} options 配置选项
	 */
	setMeasureArea : function( enable, options ) {
		this._setDrawOverlay( "measureArea", enable, options, false, false );
	},
	/**
	 * 设置是否开启热力图
	 * @param {boolean} enable 是否开启
	 * @param {Object} options 配置选项 例：{radius: 50,max: 100, data: [{lng: 116.405285, lat: 39.904989, count: 65},{}, …]}
	 */
	setHeatmap : function ( enable ,options) {
		this._getHeatmap( options, function( heatmap ){
			if( options && options.data ){
				heatmap.setDataSet( options );
			}
			if( enable ) {
				heatmap.show();
			}else{
				heatmap.hide();
			}
		} );
	},


	/**
	 * 添加覆盖物
	 * @method addOverlay
	 * @param {HQMap.Overlay} overlay 覆盖物实例
	 */
	addOverlay : function( overlay ) {
		overlay.setMap( this );
		if(overlay.showAlways){
			this.showAlwayss.put( overlay.getId(), overlay );
		}else{
			this.overlays.put( overlay.getId(), overlay );
		}
		if(overlay instanceof HQMap.CarMarker && this._cluster){
			this._cluster.addMarker(overlay._);
		}
	},


	/**
	 * 获取覆盖物
	 * @method getOverlay
	 * @param {string} overlayId 覆盖物编号 如果不存在返回覆盖物的hash对象
	 * @return  HQMap.Overlay | HQMap.HashMap
	 */
	getOverlay : function( overlayId ) {
		if( overlayId ) {
			return this.overlays.get( overlayId );
		} else {
			return this.overlays;
		}
	},

	/**
	 * 获取仅在地图上显示的，但不被地图覆盖物
	 * @method getOverlay
	 * @param {string} overlayId 覆盖物编号 如果不存在返回覆盖物的hash对象
	 * @return  HQMap.Overlay | HQMap.HashMap
	 */
	getShowAlways : function( overlayId ) {
		if( overlayId ) {
			return this.showAlwayss.get( overlayId );
		} else {
			return this.showAlwayss;
		}
	},


	/**
	 * 删除覆盖物
	 * @method removeOverlay
	 * @param {(string | HQMap.Overlay)} overlay 覆盖物编号或对象
	 */
	removeOverlay : function( overlay ) {
		if( typeof overlay === "string" || typeof overlay === "number" ) {
			overlay = this.getOverlay( overlay );
		}
		if( overlay ) {
			if(this._cluster){
				this._cluster.removeMarker(overlay._);
			}
			overlay.setMap( null );
			this.overlays.remove( overlay.getId() );
		}
	},


	/**
	 * 清除覆盖物
	 * @param clearshowAlways
	 * @param {(boolean)} clearshowAlways 删除仅在地图上显示的覆盖物，如测距功能产生的覆盖物
	 */
	clearOverlay : function(clearshowAlways) {
		if(this.overlays){
			if(this._cluster){
				this._cluster.clearMarkers();
			}
			this.overlays.each( function( overlayId, overlay ) {
				overlay.setMap( null );
			} );
			this.overlays.clear();
		}
		if(clearshowAlways && this.showAlwayss){
			this.showAlwayss.each( function( overlayId, overlay ) {
				overlay.setMap( null );
			} );
			this.showAlwayss.clear();
		}
	},

	/**
	 * 删除地图上所有的覆盖物
	 * @method clearMap
	 */
	clearMap: function() {
		if(this._cluster){
			this._cluster.clearMarkers();
		}
		this._.clearMap();
	},

	/**
	 * 设置标注点聚合
	 * @method setClusterer
	 */
	setClusterer : function(showCluster,markers){
		this.showCluster = showCluster;
		if(!markers && this.overlays){
			markers = [];
			this.overlays.each(function(key,e){
				markers.push(e._);
			});
		}
		var me = this;
		if($.isArray(markers)){
			if( me._cluster ) {
				if( me.showCluster ) {
					me._cluster.setMap( me._ );
					me._cluster.setMarkers(markers);
				} else {
					me._cluster.setMap( null );
					for(var i = 0 ;i < markers.length; i++){
						markers[i].setMap(me._);
					}
				}
			} else {
				this._.plugin( "AMap.MarkerClusterer", function() {
					me._cluster = new AMap.MarkerClusterer();
					if( me.showCluster ) {
						me._cluster.setMap( me._ );
						me._cluster.setMarkers(markers);
					} else {
						me._cluster.setMap( null );
						me._cluster.removeMarkers(markers);
					}
				});
			}
		}
	},

	/**
	 * 地理编码
	 * @method getLocation
	 * @param {string} address 地址
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {Object} geocoderOptions 地理编码参数
	 * @param {string} geocoderOptions.city 城市，地理编码时，设置地址描述所在城市 可选值：城市名（中文或中文全拼）、citycode、adcode；默认值：“全国”
	 */
	getLocation: function( address, callback, geocoderOptions ) {
		var me = this;
		this._getGeocoder( geocoderOptions, function( geocoder ) {
			geocoder.getLocation( address, function( status, result ) {
				var data = {};
				if( status === "complete" ) {
					data = me._getGeoResult( result.geocodes );
				}else if ( status === "no_data" ){
					data.message = "0051"; // no data
				}else{
					data.message = "0052"; // no reason
				}
				if( callback ) {
					callback.call( me, data );
				}
			} );
		} );
	},


	/**
	 * 逆地理编码
	 * @method getAddress
	 * @param {string} address 地址
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {Object} geocoderOptions 逆地理编码参数
	 * @param {number} geocoderOptions.radius 逆地理编码时，以给定坐标为中心点，单位：米 取值范围：0-3000 默认值：1000
	 * @param {string} geocoderOptions.extensions 逆地理编码时，返回信息详略 默认值：base，返回基本地址信息；取值为：all，返回地址信息及附近poi、道路、道路交叉口等信息
	 */
	getAddress: function( location, callback, geocoderOptions ) {
		var me = this;
		if(geocoderOptions && geocoderOptions.correct){
			this.correctLocation(location.toString(),function(locations){
				this._getGeocoder( geocoderOptions, function( geocoder ) {
					var l = locations.split(",");
					l = new HQMap.LngLat( l[0], l[1] );
					geocoder.getAddress( l._, function( status, result ) {
						var obj = null;
						if( status === "complete" ) {
							obj = me._getReGeoResult( result.regeocode );
						}
						if( callback ) {
							callback.call( me, obj );
						}
					} );
				} );
			});
		}else{
			this._getGeocoder( geocoderOptions, function( geocoder ) {
				if( $.isArray( location ) ) {
					location = new HQMap.LngLat( location[0], location[1] );
				}
				geocoder.getAddress( location._, function( status, result ) {
					var obj = null;
					if( status === "complete" ) {
						obj = me._getReGeoResult( result.regeocode );
					}
					if( callback ) {
						callback.call( me, obj );
					}
				} );
			} );
		}
	},

	/**
	 * 原始经纬度纠偏
	 * @method correctLocation
	 * @param {string} location 原始经纬度 如：116.481499,39.990475
	 * @param {Function} callback 查询回调函数 函数参数为查询结果
	 * @param {options} 纠偏参数
	 */
	correctLocation: function(location,callback,options){
		var me = this;
		$.ajax({
			url: "http://restapi.amap.com/v3/assistant/coordinate/convert",
			data: "locations="+location+"&coordsys=gps&output=json&key="+me.key,
			dataType: 'jsonp',
			success: function(msg){
		     	if(msg && msg.info === "ok"){
		     		if( callback ) {
			     		callback.call(me, msg.locations);
					}
		     	}
		   	}
		});
	},

	searchDrivingRoute : function () {
	},


	/**
	 * 获取自定义配置信息
	 * @method _getCustomOptions
	 * @private
	 * @return Object
	 */
	_getCustomOptions : function() {
		var customOptions = {
			showMapType : this.showMapType,
			showOverview : this.showOverview,
			showScale : this.showScale,
			showToolbar : this.showToolbar,
			showSatellite : this.showSatellite,
			showRoadNet : this.showRoadNet,
			showTraffic : this.showTraffic
		};
		return customOptions;
	},


	/**
	 * 获取折线的配置信息
	 *
	 * @private
	 * @return {object}
	 */
	_getOptions : function() {
		var viewOptions = {
			zoom: this.zoom
		};
		if( this.center ) {
			viewOptions.center = this.center._;
		}
		var circleOptions = {
			view : new AMap.View2D( viewOptions ),
			layers: [ new AMap.TileLayer() ],
			lang : this.lang,
			isHotspot : this.isHotspot,
			resizeEnable : this.resizeEnable,
			dragEnable : this.dragEnable,
			zoomEnable : this.zoomEnable,
			doubleClickZoom : this.doubleClickZoom,
			keyboardEnable : this.keyboardEnable,
			scrollWheel : this.scrollWheel
		};
		return circleOptions;
	},


	/**
	 * 设置是否开启指定的鼠标操作
	 *
	 * @param {string} type 鼠标操作的类型
	 * @param {boolean} enable 是否开启
	 * @param {object} options 配置选项
	 * @param {boolean} once 只允许鼠标操作一次
	 * @param {boolean} clearOverlay 关闭鼠标操作时是否清除所有覆盖物
	 */
	_setDrawOverlay: function( toolType, enable, options, once, clearOverlay ) {
		var me = this;
		this._getMouseTool( function( mouseTool ){
			if( enable ) {

				if( options ) {
					mouseTool[ toolType ].call( mouseTool, options );
				} else {
					mouseTool[ toolType ].call( mouseTool );
				}

				if( once ) {
					AMap.event.addListenerOnce( mouseTool, "draw", function() {
						mouseTool.close();
					} );
				}

			} else {

				mouseTool.close();

				if( clearOverlay ) {
					me.clearOverlay();
				}

			}
		} );
	},


	_getGeoResult: function( geocodes ) {
		var me = this;
		return $.map( geocodes, function( geocode ) {
			var info = {
				formattedAddress : geocode.formattedAddress,
				adcode : geocode.adcode,
				level : geocode.level
			};

			if ( geocode.location ) {
				info.location = new HQMap.LngLat( geocode.location.getLng(), geocode.location.getLat() );
			}

			if( geocode.addressComponent ) {
				info.addressComponent = me._getAddressComponent( geocode.addressComponent );
			}

			return info;
		} );
	},


	_getAddressComponent : function( addressComponent ) {
		var info = {
			province : addressComponent.province,
			city : addressComponent.city,
			citycode : addressComponent.citycode,
			district : addressComponent.district,
			adcode : addressComponent.adcode,
			township : addressComponent.township,
			street : addressComponent.street,
			streetNumber : addressComponent.streetNumber,
			neighborhood : addressComponent.neighborhood,
			neighborhoodType : addressComponent.neighborhoodType,
			building : addressComponent.building,
			buildingType : addressComponent.buildingType
		};
		if ( addressComponent.businessAreas ) {
			info.businessAreas = $.map( addressComponent.businessAreas, function( businessArea ) {
				var info = {
					id : businessArea.id,
					name : businessArea.name
				};
				if( businessArea.location ) {
					info.location = new HQMap.LngLat( businessArea.location.getLng(), businessArea.location.getLat() );
				}
				return info;
			} );
		}
		return info;
	},


	_getReGeoResult: function( regeocode ) {
		var info = {
			formattedAddress : regeocode.formattedAddress
		};

		if( regeocode.addressComponent ) {
			info.addressComponent = this._getAddressComponent( regeocode.addressComponent );
		}

		if( regeocode.roads ) {
			info.roads = $.map( regeocode.roads, function( road ) {
				var info = {
					id : road.id,
					name : road.name,
					distance : road.distance,
					direction : road.direction
				};
				if( road.location ) {
					info.location = new HQMap.LngLat( road.location.getLng(), road.location.getLat() );
				}
				return info;
			} );
		}

		if( regeocode.crosses ) {
			info.crosses = $.map( regeocode.crosses, function( cross ) {
				var info = {
					distance : cross.distance,
					direction : cross.direction,
					first_id : cross.first_id,
					first_name : cross.first_name,
					second_id : cross.second_id,
					second_name : cross.second_name
				};
				if( cross.location ) {
					info.location = new HQMap.LngLat( cross.location.getLng(), cross.location.getLat() );
				}
				return info;
			} );
		}

		if( regeocode.pois ) {
			info.pois = $.map( regeocode.pois, function( poi ) {
				var info = {
					id : poi.id,
					name : poi.name,
					type : poi.type,
					tel : poi.tel,
					distance : poi.distance,
					direction : poi.direction,
					address : poi.address,
					businessArea : poi.businessArea
				};
				if( poi.location ) {
					info.location = new HQMap.LngLat( poi.location.getLng(), poi.location.getLat() );
				}
				return info;
			} );
		}

		return info;
	},


	/**
	 * 获取图层信息
	 *
	 * @private
	 * @return Array.<AMap.TileLayer>
	 */
	_getLayers : function() {
		var layers = [ new AMap.TileLayer() ];
		if( this.showSatellite ) {
			layers.push( new AMap.TileLayer.Satellite() );
		}
		if( this.showRoadNet ) {
			layers.push( new AMap.TileLayer.RoadNet() );
		}
		if( this.showTraffic ) {
			layers.push( new AMap.TileLayer.Traffic() );
		}
		return layers;
	},

	/**
	 * 获取地理编码工具
	 */
	_getGeocoder : function( geocoderOptions, callback ) {
		var me = this;
        AMap.service("AMap.Geocoder", function() {
        	if( geocoderOptions ) {
        		me._geocoder = new AMap.Geocoder( geocoderOptions );
        	} else {
        		me._geocoder = new AMap.Geocoder();
        	}

        	if( callback ) {
        		callback.call( me, me._geocoder );
        	}
        });
	},


	/**
	 * 获取热力图插件
	 * @param {object} options 热力图配置
	 * @param {function} callback 回调函数 函数参数为热力图工具对象
	 */
	_getHeatmap : function( options, callback ) {
		var me = this;
        options = $.extend({  radius: 25, 	// 默认半径
        			opacity: [0, 0.8] 		// 默认透明度
        			},
        			options);
		if( me._heatmap ) {
			me._heatmap.setOptions(options);
			if( callback ) {
				callback.call( me, me._heatmap );
			}
		}else{
			this._.plugin(["AMap.Heatmap"], function () {
				me._heatmap = new AMap.Heatmap(me._,options);
				if( callback ) {
					callback.call( me, me._heatmap );
				}
			});
		}
	},

	/**
	 * 获取测距工具对象
	 *
	 * @private
	 * @param {object} options 测距工具配置
	 * @param {function} callback 回调函数 函数参数为测距工具对象
	 */
	_getRangingTool : function( options, callback ) {
		if( this._rangingTool ) {
			this._rangingTool.turnOff();
		}

		var me = this;
		this._.plugin( "AMap.RangingTool", function() {

			if( options ) {
				var rangingToolOptions = {
					startMarkerOptions : options.startMarkerOptions,
					midMarkerOptions : options.midMarkerOptions,
					endMarkerOptions : options.endMarkerOptions,
					lineOptions : options.lineOptions,
					tmpLineOptions : options.tmpLineOptions,
					startLabelText : options.startLabelText,
					midLabelText : options.midLabelText,
					endLabelText : options.endLabelText,
					bubble:null
				};
				if( options.startLabelOffset ) {
					rangingToolOptions.startLabelOffset = options.startLabelOffset._;
				}
				if( options.midLabelOffset ) {
					rangingToolOptions.midLabelOffset = options.midLabelOffset._;
				}
				if( options.endLabelOffset ) {
					rangingToolOptions.endLabelOffset = options.endLabelOffset._;
				}
				//me._rangingTool = new AMap.RangingTool( me._, rangingToolOptions );
				me._rangingTool = new AMap.RangingTool( me._ );
			} else {
				me._rangingTool = new AMap.RangingTool( me._ );
			}

			AMap.event.addListener( me._rangingTool, "addnode", function( event ) {
	    		me.trigger( HQMap.Map.event.RANGING_ADDNODE, {
	    			marker : new HQMap.Marker( { map: me,showAlways: true }, event.marker ),
	    			position : new HQMap.LngLat( event.position.getLng(), event.position.getLat() )
	    		} );
			}, me );

			AMap.event.addListener( me._rangingTool, "removenode", function( event ) {
				me.trigger( HQMap.Map.event.RANGING_REMOVENODE );
			}, me );

			AMap.event.addListener( me._rangingTool, "end", function( event ) {
	    		me.trigger( HQMap.Map.event.RANGING_END, {
	    			polyline : new HQMap.Polyline( { map: me ,showAlways: true }, event.polyline ),
	    			points : $.map( event.points, function( point ) {
	    				return new HQMap.LngLat( point.getLng(), point.getLat() );
	    			} ),
	    			distance : event.distance
	    		} );
			}, me );

			if( callback ) {
				callback.call( me, me._rangingTool );
			}
		});
	},


	/**
	 * 获取鼠标工具对象
	 *
	 * @private
	 * @param {function} callback 回调函数 函数参数为鼠标工具对象
	 */
	_getMouseTool : function( callback ) {
		if( this._mouseTool ) {
			if( callback ) {
				callback.call( this, this._mouseTool );
			}
		} else {
			var me = this;
			this._.plugin( "AMap.MouseTool", function() {
				me._mouseTool = new AMap.MouseTool( me._ );
				AMap.event.addListener( me._mouseTool, "draw", function( event ) {
					var param = {};
					if( event.obj ) {
						var obj = null;
						var eventName = null;

						if( event.obj instanceof AMap.Circle ) {
							eventName = HQMap.Map.event.DRAW_CIRCLE;
							obj = new HQMap.Circle( { map: this }, event.obj );
						}
						if( event.obj instanceof AMap.Marker ) {
							eventName = HQMap.Map.event.DRAW_MARKER;
							obj = new HQMap.Marker( { map: this }, event.obj );
						}
						if( event.obj instanceof AMap.Polyline ) {
							eventName = HQMap.Map.event.DRAW_POLYLINE;
							obj = new HQMap.Polyline( { map: this }, event.obj );
						}
						if( event.obj instanceof AMap.Polygon ) {
							eventName = HQMap.Map.event.DRAW_POLYGON;
							obj = new HQMap.Polygon( { map: this }, event.obj );
						}

						param.type = eventName;
						if( obj ) {
							param.obj = obj;
						}
					}

					if( eventName ) {
						me.trigger( eventName, param );
					}

				}, me );

				if( callback ) {
					callback.call( me, me._mouseTool );
				}
			});
		}
	},
	/**
	 * 移除鼠标工具
	 */
	_removeMouseTool : function() {
		if (this._mouseTool) {
			this._mouseTool.close(true);
		}
	},
	/**
	 * 获取驾车路线
	 * @param start 起点地址
	 * @param end 终点地址
	 */
	_getDriving : function(options, start, end, callback) {
		var me = this;
		AMap.service(["AMap.Driving"], function() {
			var driving = new AMap.Driving({
	            map: me._,
	            extensions: "base"
	        });

			driving.search([{keyword:start},
			                {keyword:end}],  function(status, results){
				var routes = results.routes;
				var rs = [];
				if(routes.length > 0) {
					var driveRoute = routes[0];
					var steps = driveRoute.steps;
					for(var i=0; i<steps.length; i++) {
						var path = steps[i].path;
						for(var j=0; j<path.length; j++) {
							rs.push(new AMap.LngLat(path[j].lng, path[j].lat));
						}
					}
				}
				if(callback) {
					callback(rs);
				}
			});
		});
	},
	/**
	 * 支持拖拽的线路规划
	 * @param start 起始地点
	 * @param end 结束地点
	 * @callback 回调
	 */
	_getDragRoute : function(options, start, end, callback) {
		var me = this;
		var position = {
				start : start,
				end : end
			};
        me.getLocation(position.start, function(rs) {
        	// 查询起点
        	if(rs && rs.length > 0) {
        		position.start = new AMap.LngLat(rs[0].location.lng,
        				rs[0].location.lat);
        	}else{
        		// 起点异常
        		position.error = 1;
        	}
        	me.getLocation(position.end, function(_rs) {
        		// 查询终点
        		if(_rs && _rs.length > 0) {
       				position.end = new AMap.LngLat(_rs[0].location.lng,
           				_rs[0].location.lat);
       			}else{
       				// 终点异常
       				position.error = position.error == 1 ? 3 : 2;
       			}
       			if(position.error == 1){
					$.messager.alert('提示', '您输入起点名称不存在，请重新输入！', 'info');
				}else if (position.error == 2){
					$.messager.alert('提示', '您输入终端名称不存在，请重新输入！', 'info');
				}else if(position.error == 3){
					$.messager.alert('提示', '您输入起点和终端名称不存在，请重新输入！', 'info');
				}else{
					me._.plugin("AMap.DragRoute", function() {
            			var path = [position.start, position.end];
            			var routeHandler = new AMap.DragRoute(me._,
            								path, options.drivingPolicy,
            								{polyOptions: options.lineOptions});
            			routeHandler.search();
            			var rs = [];
            			AMap.event.addListener(routeHandler, "complete", function(e) {
            				rs = [];
            				if(e.data && e.data.routes && (e.data.routes.length > 0)) {
            					var route = e.data.routes[0];
            					var steps = route.steps;
            					if(route && steps && steps.length > 0) {
                    				for(var i=0; i<steps.length; i++) {
                    					var path = steps[i].path;
                    					for(var j=0; j<path.length; j++) {
                    						rs.push(new HQMap.LngLat(path[j].lng, path[j].lat));
                    					}
                    				}
                    			}
            				}
            				if(callback) {
                				callback(rs);
                			}
            			});
            		});
				}
        	});
        }, {});
	}

} );


/**
 * 点标记
 *
 * @class Marker
 */
HQMap.Marker = HQMap.Overlay.extend({

    statics : {
	    event : $.extend({}, HQMap.Overlay.event, {
	        MOVING : "moving",
	        MOVEEND : "moveend",
	        MOVEALONG : "movealong"
	    })
    },

    /**
	 * 图标旋转角度
	 *
	 * @property angle
	 * @type Number
	 */
    angle : 0,

    /**
	 * 点标记显示内容，可以是HTML要素字符串或者HTML DOM对象。content有效时，icon属性将被覆盖
	 *
	 * @property content
	 * @type Object
	 */
    content : null,

    /**
	 * 点标记在地图上显示的位置，默认为地图中心点
	 *
	 * @property position
	 * @type HQMap.LngLat
	 */
    position : null,

    /**
	 * 点标记显示位置偏移量，默认值为Pixel(-10,-34)。Marker指定position后，
	 * 默认以marker左上角位置为基准点，对准所给定的position位置，若需使marker
	 * 指定位置对准在position处，需根据marker的尺寸设置一定的偏移量。
	 *
	 * @property offset
	 * @type HQMap.Pixel
	 */
    offset : null,

    /**
	 * 需在点标记中显示的图标。可以是一个本地图标地址，或者Icon对象。有合法的content内容时，此属性无效
	 *
	 * @property icon
	 * @type String|HQMap.Icon
	 */
    icon : null,

    /**
	 * 鼠标滑过点标记时的文字提示，不设置则鼠标滑过点标无文字提示
	 *
	 * @property title
	 * @type String
	 */
    title : null,

	/**
	 * 是否自动旋转。点标记在使用moveAlong动画时，路径方向若有变化，点标记是否自动调整角度，默认为false
	 * @property autoRotation
	 * @type Boolean
	 * @default false
	 */
	autoRotation: false,

    /**
	 * 标签{label.offset 父div相对于maker的位置; label.content 标签内容}
	 *
	 * @property label
	 * @type Object
	 */
    label : null,

    /**
	 * 构造函数
	 * @constructor
	 * @param {Object} options 配置选项
	 */
    constructor : function(options, _) {
	    $.extend(this, options || {});
	    this._ = _ ? _ : new AMap.Marker(this._getOptions());
	    this._super(arguments);
    },

    /**
	 * 设置图标旋转角度,由于AMPA 目前标注点选中支持IE9以上，且旋转后label跟随旋转，固先不实现该方法
	 *
	 * @method setAngle
	 * @param {Number} 角度
	 */
    setAngle : function(angle) {
    	this.angle = angle;
    	this._.setAngle(angle);
    },

    /**
	 * 返回图标旋转角度
	 *
	 * @method getAngle
	 * @return {number} 角度
	 */
    getAngle : function() {
	    return this.angle;
    },

    /**
	 * 设置图标
	 *
	 * @method setIcon
	 * @param {String|HQMap.Icon} icon 图标地址
	 */
    setIcon : function(icon) {
	    this.icon = icon;
	    this._.setIcon(this.icon);
    },

    /**
	 * 获取设置的图标
	 *
	 * @method getIcon
	 * @return string|HQMap.Icon
	 */
    getIcon : function() {
	    return this.icon;
    },

    /**
	 * 设置位置
	 *
	 * @method setPosition
	 * @param {HQMap.LngLat} position 位置信息
	 */
    setPosition : function(position) {
	    if ($.isArray(position)) {
		    this.position = new HQMap.LngLat(position[0], position[1]);
	    } else {
		    this.position = position;
	    }
	    this._.setPosition(this.position._);
    },

    /**
	 * 获取位置
	 *
	 * @method getPosition
	 * @return HQMap.LngLat
	 */
    getPosition : function() {
	    var position = this._.getPosition();
	    if (position) {
		    this.position = new HQMap.LngLat(position.getLng(), position
		            .getLat());
	    }
	    return this.position;
    },

    /**
	 * 设置偏移量
	 *
	 * @method setOffset
	 * @param {HQMap.Pixel} offset 偏移量
	 */
    setOffset : function(offset) {
	    if ($.isArray(offset)) {
		    this.offset = new HQMap.Pixel(offset[0], offset[1]);
	    } else {
		    this.offset = offset;
	    }
	    this._.setOffset(this.offset);
    },

    /**
	 * 获取偏移量
	 *
	 * @method getOffset
	 * @return {HQMap.Pixel}
	 */
    getOffset : function() {
	    var offset = this._.getOffset();
	    if (offset) {
		    this.offset = new HQMap.Pixel(offset.getX(), offset.getY());
	    }
	    return this.offset;
    },

    /**
	 * 设置显示内容，可以是HTML字符串或者HTML DOM对象
	 *
	 * @method setContent
	 * @param {Object} content 内容信息
	 */
    setContent : function(content) {
	    this.content = content;
	    this._.setContent(this.content);
    },

    /**
	 * 获取显示内容
	 *
	 * @method getContent
	 * @return HTML|DOM
	 */
    getContent : function() {
	    this.content = this._.getContent();
	    return this.content;
    },

    /**
	 * 设置鼠标滑过的文字提示
	 *
	 * @method setTitle
	 * @param {String} title 提示信息
	 */
    setTitle : function(title) {
	    this.title = title;
	    this._.setTitle(this.title);
    },

    /**
	 * 获取鼠标滑过的文字提示
	 *
	 * @method getTitle
	 * @return String
	 */
    getTitle : function() {
	    this.title = this._.getTitle();
	    return this.title;
    },

    /**
	 * 设置是否置顶
	 *
	 * @method setTop
	 * @param {Boolean} isTop 是否顶置
	 */
    setTop : function(isTop) {
	    this._.setTop(isTop);
    },

    /**
	 * 获取是否已置顶
	 *
	 * @method getTop
	 * @return Boolean
	 */
    getTop : function() {
	    return this._.getTop();
    },

    /**
	 * 设置标签
	 *
	 * @method setLabel
	 * @param {Object} label 标签
	 */
    setLabel : function(label) {
	    if (typeof label === "string") {
		    label = {
			    content : label
		    };
	    }
	    this.label = label;

	    if (this.label.offset) {
		    if ($.isArray(this.label.offset)) {
			    this.label.offset = new HQMap.Pixel(this.label.offset[0],
			            this.label.offset[1]);
		    }
	    }

	    this._.setLabel(this.label);
    },

    /**
	 * 以指定的速度，沿指定的路径移动。
	 *
	 * @method moveAlong
	 * @param {Array.<HQMap.LngLat>|Array.<Array.<number>>} path 路径坐标
	 * @param {Number} speed 指定速度，单位：千米/小时
	 * @param {Function} fn 回调函数
	 * @param {Boolean} loop 是否循环执行动画，默认为false
	 */
    moveAlong : function(path, speed, fn, loop) {
	    var param = Array.prototype.slice.call(arguments);
	    param[0] = $.map(param[0], function(path) {
		    if ($.isArray(path)) {
			    return new HQMap.LngLat(path[0], path[1])._;
		    } else {
			    return path._;
		    }
	    });
	    this._.moveAlong.apply(this._, param);
    },

    /**
	 * 以给定速度移动到指定位置
	 *
	 * @method moveTo
	 * @param {HQMap.LngLat} lnglat 指定位置
	 * @param {Number} speed 指定速度，单位：千米/小时
	 * @param {function}  fn 回调函数
	 */
    moveTo : function(lnglat, speed, fn) {
	    var param = Array.prototype.slice.call(arguments);
	    param[0] = $.type(param[0]) === "object" ? param[0]._
	            : new HQMap.LngLat(param[0][0], param[0][1])._;
	    this._.moveTo.apply(this._, param);
    },

    /**
	 * 停止动画
	 * @method stopMove
	 */
    stopMove : function() {
	    this._.stopMove();
    },

    /**
	 * 显示
	 * @method show
	 * @override
	 */
    show : function() {
	    HQMap.Overlay.prototype.show.call(this);

	    this.trigger(HQMap.Marker.event.SHOW, new HQMap.EventObject({
	        type : HQMap.Marker.event.SHOW,
	        target : this
	    }));
    },

    /**
	 * 隐藏
	 * @method hide
	 * @override
	 */
    hide : function() {
	    HQMap.Overlay.prototype.hide.call(this);

	    this.trigger(HQMap.Marker.event.HIDE, new HQMap.EventObject({
	        type : HQMap.Marker.event.HIDE,
	        target : this
	    }));
    },

    /**
	 * 获取配置信息
	 * @method _getOptions
	 * @private
	 * @return Object
	 */
    _getOptions : function() {
	    var circleOptions = {
	        autoRotation : false,
	        zIndex : this.zIndex,
	        draggable : this.draggable,
	        clickable : this.clickable,
	        visible : this.visible,
	        bubble : this.bubble
	    };

	    if(this.autoRotation) {
	    	circleOptions.autoRotation = this.autoRotation;
	    }
	    if (this.map) {
		    circleOptions.map = this.map._;
	    }
	    if (this.content) {
		    circleOptions.content = this.content;
	    }
	    if (this.position) {
		    if ($.isArray(this.position)) {
			    this.position = new HQMap.LngLat(this.position[0],
			            this.position[1]);
		    }
		    circleOptions.position = this.position._;
	    }
	    if (this.offset) {
		    if ($.isArray(this.offset)) {
			    this.offset = new HQMap.Pixel(this.offset[0], this.offset[1]);
		    }
		    circleOptions.offset = this.offset._;
	    }
	    if (this.icon) {
		    circleOptions.icon = $.type(this.icon) === "object" ? this.icon._
		            : this.icon;
	    }
	    if (this.title) {
		    circleOptions.title = this.title;
	    }
	    if (this.label) {
		    if (this.label.offset) {
			    if ($.isArray(this.label.offset)) {
				    this.label.offset = new HQMap.Pixel(this.label.offset[0],
				            this.label.offset[1]);
			    }
		    }
		    circleOptions.label = this.label;
	    }
	    return circleOptions;
    }

});
/**
 * 多边形
 * @class Polygon
 */
HQMap.Polygon = HQMap.AreaOverlay.extend( {


	/**
	 * 多边形轮廓线的节点坐标数组
	 * @property path
	 * @type Array.<HQMap.LngLat> | Array.<Array.<HQMap.LngLat>>
	 */
	path: [],


	/**
	 * 是否可编辑
	 * @property editable
	 * @type Boolean
	 * @default false
	 */
	editable: false,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {Object} options  配置选项
	 */
    constructor : function( options, _ ) {
    	$.extend( this, options || {} );
    	this._ = _ ? _ : new AMap.Polygon( this._getOptions() );
    	this._super( arguments );

    	this.set( this._getCustomOptions() );
    },


	/**
	 * 设置组成该折线的节点数组
	 * @method setPath
	 * @param {(Array.<HQMap.LngLat> | Array.<Array.<HQMap.LngLat>>)} path
	 */
	setPath: function( path ) {
		this.path = $.map( path, function( path ) {
			if( $.isArray( path ) ) {
				if( path.length && $.isNumeric( path[ 0 ] ) ) {
					return new HQMap.LngLat( path[ 0 ], path[ 1 ] );
				} else {
					return [ $.map( path, function( innerPath ) {
						if( $.isArray( innerPath ) ) {
							return new HQMap.LngLat( innerPath[ 0 ], innerPath[ 1 ] )
						} else {
							return innerPath;
						}
					} ) ];
				}
			} else {
				return path;
			}
		});
		this._.setPath( $.map( this.path, function( path ) {
			if( $.isArray( path ) ) {
				return [ $.map( path, function( innerPath ) {
					return innerPath._;
				} ) ];
			} else {
				return path._;
			}
		} ) );
	},


	/**
	 * 获取多边形轮廓线节点数组
	 * @method getPath
	 * @return Array.<HQMap.LngLat> | Array.<Array.<HQMap.LngLat>>
	 */
	getPath: function() {
		this.path = $.map( this._.getPath(), function( p ) {
			if( $.isArray( p ) ) {
				return [ $.map( p, function( pInner ) {
					return new HQMap.LngLat( pInner.getLng(), pInner.getLat() );
				} ) ];
			} else {
				return new HQMap.LngLat( p.getLng(), p.getLat() );
			}
		});
		return this.path;
	},


	/**
	 * 设置是否可编辑
	 * @method setEditable
	 * @param {Boolean} editable 是否可编辑
	 */
	setEditable : function( editable ) {
		this.editable = editable;

		if( this._editer ) {
			if( this._editer ) {
				if( this.editable ) {
					this._editer.open();
				} else {
					this._editer.close();
				}
			}
		} else {
			if( this.map ) {
				var me = this;
				this.map._.plugin( "AMap.PolyEditor", function() {
					me._editer = new AMap.PolyEditor( me.map._, me._ );
					if( me._editer ) {
						if( me.editable ) {
							me._editer.open();
						} else {
							me._editer.close();
						}
					}
				} );
			}
		}
	},


	/**
	 * 获取多边形的面积（单位：平方米）
	 * @method getArea
	 * @return Number
	 */
	getArea: function() {
		return this._.getArea();
	},


	/**
	 * 获取自定义配置信息
	 * @method _getCustomOptions
	 * @private
	 * @return Object
	 */
	_getCustomOptions : function() {
		var customOptions = {
			editable : this.editable
		};
		return customOptions;
	},


	/**
	 * 获取配置信息
	 * @method _getOptions
	 * @private
	 * @return Object
	 */
	_getOptions : function() {
		var circleOptions = {
			path : $.map( this.path, function( path ) {
				if( $.isArray( path ) ) {
					if( path.length && $.isNumeric( path[ 0 ] ) ) {
						return new HQMap.LngLat( path[ 0 ], path[ 1 ] )._;
					} else {
						return [ $.map( path, function( innerPath ) {
							if( $.isArray( innerPath ) ) {
								return new HQMap.LngLat( innerPath[ 0 ], innerPath[ 1 ] )._;
							} else {
								return innerPath._;
							}
						} ) ];
					}
				} else {
					return path._;
				}
			}),
			zIndex : this.zIndex,
			draggable : this.draggable,
			clickable : this.clickable,
			visible : this.visible,
			bubble : this.bubble,
			strokeColor : this.strokeColor,
			strokeOpacity : this.strokeOpacity,
			strokeWeight : this.strokeWeight,
			strokeStyle : this.strokeStyle,
			fillColor : this.fillColor,
			fillOpacity : this.fillOpacity
		};
		if( this.map ) {
			circleOptions.map = this.map._;
		}
		return circleOptions;
	}


} );
/**
 * 折线
 * @class Polyline
 */
HQMap.Polyline = HQMap.LineOverlay.extend( {


	/**
     * 组成该折线的节点数组
	 * @property path
	 * @type Array.<HQMap.Lnglat>
	 */
	path: [],


	/**
	 * 是否可编辑
	 * @property editable
	 * @type Boolean
	 * @default false
	 */
	editable: false,


	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {object} options 配置选项
	 */
    constructor : function( options, _ ) {
    	$.extend( this, options || {} );
    	this._ = _ ? _ : new AMap.Polyline( this._getOptions() );
    	this._super( arguments );

    	this.set( this._getCustomOptions() );
    },


	/**
	 * 设置组成该折线的节点数组
	 * @method setPath
	 * @param { Array.<HQMap.Lnglat>} path 路径集合
	 */
	setPath: function( path ) {
		this.path = $.map( path, function(p) {
			if( $.isArray( p ) ) {
				return new HQMap.LngLat( p[ 0 ], p[ 1 ] );
			} else {
				return p;
			}
		});
		this._.setPath( $.map( this.path, function( p ) {
			return p._;
		} ) );
	},


	/**
	 * 获取折线路径的节点数组
	 * @method getPath
	 * @return Array.<HQMap.Lnglat>
	 */
	getPath: function() {
		this.path = $.map( this._.getPath(), function( path ) {
			return new HQMap.LngLat( path.getLng(), path.getLat() );
		} );
		return this.path;
	},


	/**
	 * 设置是否可编辑
	 * @method setEditable
	 * @param {Boolean} editable
	 */
	setEditable : function( editable ) {
		this.editable = editable;

		if( this._editer ) {
			if( this._editer ) {
				if( this.editable ) {
					this._editer.open();
				} else {
					this._editer.close();
				}
			}
		} else {
			if( this.map ) {
				var me = this;
				this.map._.plugin( "AMap.PolyEditor", function() {
					me._editer = new AMap.PolyEditor( me.map._, me._ );
					if( me._editer ) {
						if( me.editable ) {
							me._editer.open();
						} else {
							me._editer.close();
						}
					}
				} );
			}
		}
	},


	/**
	 * 获取折线的总长度（单位：米）
	 * @method getLength
	 * @return Number
	 */
	getLength: function() {
		return this._.getLength();
	},


	/**
	 * 获取自定义配置信息
	 * @method _getCustomOptions
	 * @private
	 * @return Object
	 */
	_getCustomOptions : function() {
		var customOptions = {
			editable : this.editable
		};
		return customOptions;
	},


	/**
	 * 获取折线的配置信息
	 * @method _getOptions
	 * @private
	 * @return Object
	 */
	_getOptions : function() {
		var polylineOptions = {
			path : $.map( this.path, function( p ) {
				if( $.isArray( p ) ) {
					return new HQMap.LngLat( p[ 0 ], p[ 1 ] )._;
				} else {
					return p._;
				}
			} ),
			zIndex : this.zIndex,
			draggable : this.draggable,
			clickable : this.clickable,
			visible : this.visible,
			bubble : this.bubble,
			strokeColor : this.strokeColor,
			strokeOpacity : this.strokeOpacity,
			strokeWeight : this.strokeWeight,
			strokeStyle : this.strokeStyle,
			isOutline: this.isOutline,
			outlineColor: this.outlineColor
		};
		if( this.map ) {
			polylineOptions.map = this.map._;
		}
		return polylineOptions;
	}


} );
/**
 * 汽车标记
 * @module HQMap
 * @class CarMarker
 * @namespace HQMap
 * @extends HQMap.Marker
 */
HQMap.CarMarker = HQMap.Marker.extend( {


    statics : {
    	event : HQMap.Marker.event,
    	markerTpl : [
			'<div class="marker-car" style="text-align: left;">',
				'<img class="car-image"/>',
				'<div class="car-label"><div class="car-label-content" style="width :100%;border: 0;white-space: nowrap;"></div></div>',
			'</div>'
      	].join('')
    },


    /**
     * 图标旋转角度
     * @porperty angle
     * @type Number
     */
    angle : 0,


    /**
     * 车辆图表
     * @porperty icon
     * @type String
     */
    icon : "http://code.mapabc.com/images/car_03.png",


    /**
     * 标签内容
     * @property label
     * @type String
     */
    label :  "",


	/**
	 * 构造函数
	 * @class CarMarker
	 * @constructor
	 * @param {object} options 配置选项
	 */
    constructor : function( options ) {
    	if( !options.offset ) {
    		options.offset = [ 0, 0 ];
    	}
     	this._super( arguments );

     	this.getContent();
     	if( this.label ) {
     		this.setLabel( this.label );
     	}
    	this.setAngle( this.angle );
    	this.setOffset(options.offset);
    },


    /**
     * 设置图标旋转角度
     * @method setAngle
     * @param {number} angle 角度值
     */
    setAngle : function( angle ) {
		this.angle = angle;
		this.getContent().find(".car-image").rotate( this.angle );
    },


    /**
     * 设置车辆图标
     * @method setIcon
     * @param {string} icon 图标地址
     */
    setIcon : function( icon ) {
		this.icon = icon;
		var content = this.getContent();
		content.find(".car-image").remove();
		content.prepend( this.backCarImage.clone().attr( "src", this.icon ) );

		this.setAngle(this.angle);
    },

    /**
     * 获取图标
     * @method getIcon
     */
    getIcon : function() {
    	return this.icon;
    },

	/**
	 * 设置标签内容
	 * @method setLabel
	 * @param {string} label 标签内容
	 */
	setLabel : function( label ) {
		this.label = label;
		this.getContent().find(".car-label-content").html( this.label );
	},


	/**
	 * 获取标记内容
	 * @method getContent
	 * @override
	 * @return {object} 标记的jquery对象
	 */
	getContent : function() {
    	if ( !this.content ) {
    		this.setContent( $( HQMap.CarMarker.markerTpl ).appendTo( document.body )[0] );
    		this.content = $( HQMap.Marker.prototype.getContent.call(this) );
    		this.backCarImage = this.content.find(".car-image").clone();
    		this.content.find(".car-image").attr( "src", this.icon );
    	}
    	return this.content;
    }


} );
/**
 * 地图
 * @module HQMap
 * @namespace HQMap
 * @class CommonMap
 */
HQMap.CommonMap = HQMap.Map.extend( {


	/**
	 * 系统静态方法,不需要依赖地图对象
	 */
	statics : {
		/**
		 * 地理位置编码
		 */
	    geocoder : new HQMap.Geocoder(),
	    /**
		 * 逆地理位置编码
		 */
	    getAddress : function( location, callback, geocoderOptions ){
	    	this.geocoder.getSimpleAddress(location, callback, geocoderOptions );
	    },
	    /**
		 * 根据关键字搜索地理位置查找
		 */
	    getLocation : function( keyword, callback, placeSearchOptions ){
	    	this.geocoder.search(keyword, callback, placeSearchOptions );
	    },
	    /**
		 * 地理位置编码
		 */
	    getGeLocation : function( address, callback, geocoderOptions ){
	    	this.geocoder.search(address, callback, geocoderOptions );
	    },
	    /**
		 * 根据中心点经纬度、半径以及关键字进行周边查询
		 */
	    searchNearBy : function( keyword,center,radius,callback, placeSearchOptions ){
	    	placeSearchOptions.map = placeSearchOptions.map._ ;
	    	this.geocoder.searchNearBy(keyword,center,radius,callback, placeSearchOptions  );
	    }
    },

    /**
	  * 窗体实例
	  * @property _infoWindow
	  * @type HQMap.InfoWindow
	 */
	_infoWindow : new HQMap.InfoWindow(),

	/**
	 * 构造函数
	 *
	 * @constructor
	 * @param {object} options - 配置选项
	 */
    constructor : function( mapContainer, options ) {
    	this._super( arguments );
    },

    /**
	 * 气泡关闭监听
	 * @method tipCloseListener
	 * @param {Function} callback  气泡关闭回调
	 */
	tipCloseListener : function(callback){
		this._infoWindow.on(HQMap.InfoWindow.event.CLOSE,callback)
	},

	/**
	 * 根据关键字搜索地理位置查找
	 * @param {String} keyword 关键字
	 * @param {Function} callback 回调函数
	 * @param {Object} geocoderOptions 配置信息
	 * @method getLocation
	 */
	getLocation : function( keyword, callback, geocoderOptions ){
    	HQMap.CommonMap.getLocation(keyword, callback, geocoderOptions );
    },
    /**
	 * 地理位置编码
	 */
	getGeLocation : function( address, callback, geocoderOptions ){
    	HQMap.CommonMap.getGeLocation(address, callback, geocoderOptions );
    },
    /**
	 * 根据中心点经纬度、半径以及关键字进行周边查询
	 */
    searchNearBy : function( keyword,center,radius,callback, placeSearchOptions ){
    	var opts = $.extend({map:this} , placeSearchOptions);
    	HQMap.CommonMap.searchNearBy(keyword,center,radius,callback, opts);
    },
	/**
	 * 逆地理位置编码
	 * @param {String} location 位置信息
	 * @param {Function} callback 回调函数
	 * @param {Object} geocoderOptions 配置信息
	 * @method getAddress
	 */
	getAddress: function( location, callback, geocoderOptions ) {
		var me = this;
		var fn = function(info){
			var address = "";
			if(info){
				var c = info.addressComponent;
				if(c){
					address += c.province + c.city;
					if(c.district){
						address += c.district;
					}
					if(c.township){
						address += c.township;
					}
				}
				if(info.roads && info.roads.length > 0){
					address += info.roads[0].name;
				}
				if(info.pois && info.pois.length > 0){
					var poi = info.pois[0];
					var direction = "";
					switch(poi.direction){
						case '北':
							direction = '南';
						break;
						case '南':
							direction = '北';
						break;
						case '西':
							direction = '东';
						break;
						case '东':
							direction = '西';
						break;
						case '西南':
							direction = '东北';
						break;
						case '东北':
							direction = '西南';
						break;
						case '东南':
							direction = '西北';
						break;
						case '西北':
							direction = '东南';
						break;
						default:
							'';
					}
					address += poi.name + direction + Math.round(poi.distance) + '米';
				}
			}else{
				address = "";
			}
			if(callback){
				callback.call( me, address,geocoderOptions);
			}
		}
		this._super([location,fn,$.extend({extensions:'all'},geocoderOptions||{})]);
	},

	/**
	 *根据覆盖物ID获取地图覆盖物
	 *@method getOverlayById
	 *@param {String} overlayId 覆盖物ID
	 */
	getOverlayById : function( overlayId ){
		return this.getOverlay( overlayId );
	},


	/**
	  * 更新覆盖物信息
	  * @method updateOverlay
	  * @param {Overlay} overlay 覆盖物
	  * @param {Object} options 覆盖物配置项
	  */
	updateOverlay : function( overlay, options ) {
	},


	/**
	  * 打开地图气泡
	  * @method openTip
	  * @param {HQMap.LngLat} position 位置信息
	  * @param {Object} tipOption 配置项
	  */
	openTip : function(position, tipOption){
		if(position){
			if(tipOption) {
				if(tipOption.tipWidth && tipOption.tipHeight){
					tipOption.size = new HQMap.Size(tipOption.tipWidth,tipOption.tipHeight);
				}
				tipOption = $.extend({ offset : [ 0, 0 ],position : position },tipOption);
				this._infoWindow.set(tipOption);
				// 设置地图中心点，默认为气泡位置
				var center = new HQMap.LngLat(position.getLng(),position.getLat());
				// 设置地图缩放级别
				var zoom = this.getZoomLevel() < 9 ? 9 : this.getZoomLevel();
				// 地图中心点像素坐标
				var pcenter = this.lngLatToContainer(this.getCenter(),zoom);
				// 判断地图中心点像素高度是否可以完全显示气泡,不能完全显示则将像素点下移
				if(pcenter.getY() < tipOption.tipHeight + 100){
					pcenter.setY(tipOption.tipHeight + 100);
					// 像素点转换为经纬度坐标
					var cref = this.containerToLngLat(pcenter , zoom);
					center.setLat(this.getCenter().getLat() / cref.getLat() * position.getLat());
				}
				// 根据当前地图中心点及缩放级别设置
				this.setZoomAndCenter(zoom,center);
			}
			if(!this._infoWindow.getIsOpen()){
				// 延迟打开气泡
				var me = this;
				setTimeout(function(){me._infoWindow.open(me, position);},1000);
			}
		}
	},


	/**
	 * 关闭气泡
	 * @method closeTip
	 */
	closeTip : function() {
		if( this._infoWindow.getIsOpen() ) {
			this._infoWindow.close();
		}
	},

	/**
	 * 在覆盖物上打开气泡
	 * @method openOverlayTip
	 * @param {Overlay} overlay 覆盖物
	 * @param {Object} option 配置项
	 */
	openOverlayTip : function( overlay, option ) {
		if( typeof overlay === "string" || typeof overlay === "number" ) {
			overlay = this.getOverlay( overlay );
		}

		options = $.extend( {
			canShowTip : true,
			position : overlay.getPosition ? overlay.getPosition() : this.getCenter()
		},  overlay.option ||{}, option || {} );

		if( options.canShowTip ) {
			this.openTip( options.position, $.extend({
			  offset : options.offset ? options.offset : [0,0]},
			  options.tipOption || {})
			);
		}
	},


	/**
	  * 获取地图查看范围
	  * @method getLngLatBounds
	  * @return Bounds
	  */
	getLngLatBounds : function(){
		return this.getBounds();
	},

	/**
	  * 获取缩放级别
	  * @method getZoomLevel
	  */
	getZoomLevel : function(){
		return this.getZoom();
	}

} );


///**
// * 服务站注点
// */
//HQMap.ServiceMarker = function(type, id){
//	this.markerType = '服务站';
//	this.id = id;
//	if (type == 1) {
//		// 服务站
//		this.icon = '/images/map/fwz.gif';
//	} else {
//		// 自定义标注点
//		this.icon = '/images/mark_txj2.png';
//	}
//};
//

///**
// * 鸿泉地图气泡
// */
//HQMap.Tip = function() {
// 	this.autoMove = true;
//};